diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index b69608e4..00000000 --- a/.coveragerc +++ /dev/null @@ -1,8 +0,0 @@ -[run] -source = intercom - -[report] -omit = - */python?.?/* - */site-packages/nose/* - diff --git a/.fern/metadata.json b/.fern/metadata.json new file mode 100644 index 00000000..61ed89bd --- /dev/null +++ b/.fern/metadata.json @@ -0,0 +1,11 @@ +{ + "cliVersion": "3.0.2", + "generatorName": "fernapi/fern-python-sdk", + "generatorVersion": "4.41.8", + "generatorConfig": { + "client_class_name": "Intercom", + "pydantic_config": { + "skip_validation": true + } + } +} \ No newline at end of file diff --git a/.fernignore b/.fernignore new file mode 100644 index 00000000..084a8ebb --- /dev/null +++ b/.fernignore @@ -0,0 +1 @@ +# Specify files that shouldn't be modified by Fern diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..e4588ced --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,60 @@ +name: ci +on: [push] +jobs: + compile: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Set up python + uses: actions/setup-python@v4 + with: + python-version: 3.9 + - name: Bootstrap poetry + run: | + curl -sSL https://install.python-poetry.org | python - -y --version 1.5.1 + - name: Install dependencies + run: poetry install + - name: Compile + run: poetry run mypy . + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Set up python + uses: actions/setup-python@v4 + with: + python-version: 3.9 + - name: Bootstrap poetry + run: | + curl -sSL https://install.python-poetry.org | python - -y --version 1.5.1 + - name: Install dependencies + run: poetry install + + - name: Test + run: poetry run pytest -rP -n auto . + + publish: + needs: [compile, test] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Set up python + uses: actions/setup-python@v4 + with: + python-version: 3.9 + - name: Bootstrap poetry + run: | + curl -sSL https://install.python-poetry.org | python - -y --version 1.5.1 + - name: Install dependencies + run: poetry install + - name: Publish to pypi + run: | + poetry config repositories.remote https://upload.pypi.org/legacy/ + poetry --no-interaction -v publish --build --repository remote --username "$PYPI_USERNAME" --password "$PYPI_PASSWORD" + env: + PYPI_USERNAME: ${{ secrets.PYPI_USERNAME }} + PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }} diff --git a/.gitignore b/.gitignore index 5858c47e..d2e4ca80 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,5 @@ -dist -venv -.venv -.coverage -.env -*.egg-info -*.pyc -.DS_Store -docs/_build - -intercom.sublime-project -intercom.sublime-workspace +.mypy_cache/ +.ruff_cache/ +__pycache__/ +dist/ +poetry.toml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 323f894c..00000000 --- a/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -language: python -python: - - 2.7 - - 3.4 -install: - - pip install -r requirements.txt --use-mirrors - - pip install -r dev-requirements.txt --use-mirrors -script: - - nosetests --with-coverag tests/unit -after_success: - coveralls --verbose diff --git a/AUTHORS.rst b/AUTHORS.rst deleted file mode 100644 index b67e5af4..00000000 --- a/AUTHORS.rst +++ /dev/null @@ -1,29 +0,0 @@ -python-intercom is written and maintained by John Keyes and various -contributors: - -Development Lead -~~~~~~~~~~~~~~~~ - -- John Keyes `@jkeyes `_ - -Patches and Suggestions -~~~~~~~~~~~~~~~~~~~~~~~ - -- `vrachnis `_ -- `sdorazio `_ -- `Cameron Maske `_ -- `Martin-Zack Mekkaoui `_ -- `Marsel Mavletkulov `_ -- `Grant McConnaughey `_ -- `Robert Elliott `_ -- `Jared Morse `_ -- `neocortex `_ -- `jacoor `_ -- `maiiku `_ - -Intercom -~~~~~~~~ - -- `Darragh Curran `_ -- `Bill de hÓra `_ -- `Jeff Gardner `_ diff --git a/CHANGES.rst b/CHANGES.rst deleted file mode 100644 index d64e5af6..00000000 --- a/CHANGES.rst +++ /dev/null @@ -1,63 +0,0 @@ -Changelog -========= - -* 2.1.1 - * No runtime changes. -* 2.1.0 - * Adding interface support for opens, closes, and assignments of conversations. (`#101 `_) - * Ensuring identity_hash only contains variables with valid values. (`#100 `_) - * Adding support for unique_user_constraint and parameter_not_found errors. (`#97 `_) -* 2.0.0 - * Added support for non-ASCII character sets. (`#86 `_) - * Fixed response handling where no encoding is specified. (`#81 `_) - * Added support for `None` values in `FlatStore`. (`#88 `_) -* 2.0.beta - * Fixed `UnboundLocalError` in `Request.parse_body`. (`#72 `_) - * Added support for replies with an empty body. (`#72 `_) - * Fixed a bug in identifying changed attributes when creating new resources. (`#77 `_) -* 2.0.alpha - * Added support for Intercom API v2. - * Added support for Python 3. -* 0.2.13 - * Fixed wildcard import from `intercom`. (`#28 `_) -* 0.2.12 - * Added RTD theme to requirements.txt -* 0.2.11 - * Added support for events. (`#25 `_) - * Using RTD theme for documentation. - * Fixed links to Intercom API docs. -* 0.2.10 - * Added basic support for companies. (`#18 `_) - * Fixed User.delete. (`#19 `_) - * Fixed links to Intercom API docs. - * Fixed doctests. -* 0.2.9 - * Added `unsubscribed_from_emails` attribute to `User` object. (`#15 `_) - * Added support for `last_request_at` parameter in `Intercom.create_user`. (`#16 `_) - * Added support for page, per_page, tag_id, and tag_name parameters on `Intercom.get_users`. (`#17 `_) -* 0.2.8 - * Added support for tagging. (`#13 `_) - * Fixed installation into a clean python environment. (`#12 `_) - * Fixed doctest. - * Updated PEP8 formatting. -* 0.2.7 - * Fixed delete user support to send bodyless request. - * Added support for user notes. -* 0.2.6 - * Added support for delete user. -* 0.2.5 - * Fixed consistent version numbering (docs and code). -* 0.2.4 - * Fixed handling of invalid JSON responses. - * Fixed doctests to pass with current Intercom dummy API. -* 0.2.3 - * Fixed version number of distribution to match documentation. -* 0.2.2 - * Updated docstrings and doctests. -* 0.2.1 - * Added some docstrings. -* 0.2 - * Created source distribution. (`#2 `_) - * Fixed error names. (`#1 `_) -* 0.1 - * Initial release. diff --git a/INSTALL.rst b/INSTALL.rst deleted file mode 100644 index b9eff6b2..00000000 --- a/INSTALL.rst +++ /dev/null @@ -1,16 +0,0 @@ -Installation -============ - -The simplest way to install python-intercom is with `pip `_: - -:: - - pip install python-intercom - -or you may download a `.tar.gz` source archive from `pypi `_: - -:: - - tar xf python-intercom.tar.gz - cd python-intercom - python setup.py install diff --git a/LICENSE.rst b/LICENSE.rst deleted file mode 100644 index a4898aee..00000000 --- a/LICENSE.rst +++ /dev/null @@ -1,4 +0,0 @@ -MIT License -=========== - -See http://jkeyes.mit-license.org/ for specific license information. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 0ee5a822..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,4 +0,0 @@ -include *.rst -recursive-include docs *.rst -include requirements.txt -recursive-include tests *.py \ No newline at end of file diff --git a/README.md b/README.md index 70ffb2b7..5d8ded49 100644 --- a/README.md +++ b/README.md @@ -1,442 +1,205 @@ -# python-intercom +# Intercom Python Library -[ ![PyPI Version](https://img.shields.io/pypi/v/python-intercom.svg) ](https://pypi.python.org/pypi/python-intercom) [ ![PyPI Downloads](https://img.shields.io/pypi/dm/python-intercom.svg) ](https://pypi.python.org/pypi/python-intercom) [ ![Travis CI Build](https://travis-ci.org/jkeyes/python-intercom.svg) ](https://travis-ci.org/jkeyes/python-intercom) [![Coverage Status](https://coveralls.io/repos/jkeyes/python-intercom/badge.svg?branch=master)](https://coveralls.io/r/jkeyes/python-intercom?branch=master) +[![fern shield](https://img.shields.io/badge/%F0%9F%8C%BF-Built%20with%20Fern-brightgreen)](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=https%3A%2F%2Fgithub.com%2Fintercom%2Fpython-intercom) +[![pypi](https://img.shields.io/pypi/v/python-intercom)](https://pypi.python.org/pypi/python-intercom) -Python bindings for the Intercom API (https://api.intercom.io). +The Intercom Python library provides convenient access to the Intercom APIs from Python. -[API Documentation](https://api.intercom.io/docs). +## Table of Contents -[Package Documentation](http://readthedocs.org/docs/python-intercom/). +- [Installation](#installation) +- [Reference](#reference) +- [Usage](#usage) +- [Async Client](#async-client) +- [Exception Handling](#exception-handling) +- [Pagination](#pagination) +- [Advanced](#advanced) + - [Access Raw Response Data](#access-raw-response-data) + - [Retries](#retries) + - [Timeouts](#timeouts) + - [Custom Client](#custom-client) +- [Contributing](#contributing) -## Upgrading information - -Version 2 of python-intercom is **not backwards compatible** with previous versions. +## Installation -One change you will need to make as part of the upgrade is to set `Intercom.app_api_key` and not set `Intercom.api_key`. +```sh +pip install python-intercom +``` -## Installation +## Reference - pip install python-intercom +A full reference for this library is available [here](https://github.com/intercom/python-intercom/blob/HEAD/./reference.md). -## Basic Usage +## Usage -### Configure your access credentials +Instantiate and use the client with the following: ```python -Intercom.app_id = "my_app_id" -Intercom.app_api_key = "my-super-crazy-api-key" +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.ai_content.create_content_import_source( + url="https://www.example.com", +) ``` +## Async Client -### Resources - -Resources this API supports: - - https://api.intercom.io/users - https://api.intercom.io/companies - https://api.intercom.io/tags - https://api.intercom.io/notes - https://api.intercom.io/segments - https://api.intercom.io/events - https://api.intercom.io/conversations - https://api.intercom.io/messages - https://api.intercom.io/counts - https://api.intercom.io/subscriptions - -Additionally, the library can handle incoming webhooks from Intercom and convert to `intercom` models. - -### Examples - - -#### Users - -``` python -from intercom import User -# Find user by email -user = User.find(email="bob@example.com") -# Find user by user_id -user = User.find(user_id="1") -# Find user by id -user = User.find(id="1") -# Create a user -user = User.create(email="bob@example.com", name="Bob Smith") -# Delete a user -deleted_user = User.find(id="1").delete() -# Update custom_attributes for a user -user.custom_attributes["average_monthly_spend"] = 1234.56 -user.save() -# Perform incrementing -user.increment('karma') -user.save() -# Iterate over all users -for user in User.all(): - ... -``` +The SDK also exports an `async` client so that you can make non-blocking calls to our API. Note that if you are constructing an Async httpx client class to pass into this client, use `httpx.AsyncClient()` instead of `httpx.Client()` (e.g. for the `httpx_client` parameter of this client). -#### Admins +```python +import asyncio -``` python -from intercom import Admin -# Iterate over all admins -for admin in Admin.all(): - ... -``` +from intercom import AsyncIntercom -#### Companies - -``` python -from intercom import Company -from intercom import User -# Add a user to one or more companies -user = User.find(email="bob@example.com") -user.companies = [ - {"company_id": 6, "name": "Intercom"}, - {"company_id": 9, "name": "Test Company"} -] -user.save() -# You can also pass custom attributes within a company as you do this -user.companies = [ - { - "id": 6, - "name": "Intercom", - "custom_attributes": { - "referral_source": "Google" - } - } -] -user.save() -# Find a company by company_id -company = Company.find(company_id="44") -# Find a company by name -company = Company.find(name="Some company") -# Find a company by id -company = Company.find(id="41e66f0313708347cb0000d0") -# Update a company -company.name = 'Updated company name' -company.save() -# Iterate over all companies -for company in Company.all(): - ... -# Get a list of users in a company -company.users -``` +client = AsyncIntercom( + token="YOUR_TOKEN", +) -#### Tags - -``` python -from intercom import Tag -# Tag users -tag = Tag.tag_users('blue', ["42ea2f1b93891f6a99000427"]) -# Untag users -Tag.untag_users('blue', ["42ea2f1b93891f6a99000427"]) -# Iterate over all tags -for tag in Tag.all(): - ... -# Iterate over all tags for user -Tag.find_all_for_user(id='53357ddc3c776629e0000029') -Tag.find_all_for_user(email='declan+declan@intercom.io') -Tag.find_all_for_user(user_id='3') -# Tag companies -tag = Tag.tag_companies('red', ["42ea2f1b93891f6a99000427"]) -# Untag companies -Tag.untag_companies('blue', ["42ea2f1b93891f6a99000427"]) -# Iterate over all tags for company -Tag.find_all_for_company(id='43357e2c3c77661e25000026') -Tag.find_all_for_company(company_id='6') -``` -#### Segments - -``` python -from intercom import Segment -# Find a segment -segment = Segment.find(id=segment_id) -# Update a segment -segment.name = 'Updated name' -segment.save() -# Iterate over all segments -for segment in Segment.all(): - ... -``` +async def main() -> None: + await client.ai_content.create_content_import_source( + url="https://www.example.com", + ) -#### Notes - -``` python -# Find a note by id -note = Note.find(id=note) -# Create a note for a user -note = Note.create( - body="

Text for the note

", - email='joe@example.com') -# Iterate over all notes for a user via their email address -for note in Note.find_all(email='joe@example.com'): - ... -# Iterate over all notes for a user via their user_id -for note in Note.find_all(user_id='123'): - ... -``` -#### Conversations - -``` python -from intercom import Conversation -# FINDING CONVERSATIONS FOR AN ADMIN -# Iterate over all conversations (open and closed) assigned to an admin -for convo in Conversation.find_all(type='admin', id='7'): - ... -# Iterate over all open conversations assigned to an admin -for convo Conversation.find_all(type='admin', id=7, open=True): - ... -# Iterate over closed conversations assigned to an admin -for convo Conversation.find_all(type='admin', id=7, open=False): - ... -# Iterate over closed conversations for assigned an admin, before a certain -# moment in time -for convo in Conversation.find_all( - type='admin', id= 7, open= False, before=1374844930): - ... - -# FINDING CONVERSATIONS FOR A USER -# Iterate over all conversations (read + unread, correct) with a user based on -# the users email -for convo in Conversation.find_all(email='joe@example.com',type='user'): - ... -# Iterate over through all conversations (read + unread) with a user based on -# the users email -for convo in Conversation.find_all( - email='joe@example.com', type='user', unread=False): - ... -# Iterate over all unread conversations with a user based on the users email -for convo in Conversation.find_all( - email='joe@example.com', type='user', unread=true): - ... - -# FINDING A SINGLE CONVERSATION -conversation = Conversation.find(id='1') - -# INTERACTING WITH THE PARTS OF A CONVERSATION -# Getting the subject of a part (only applies to email-based conversations) -conversation.rendered_message.subject -# Get the part_type of the first part -conversation.conversation_parts[0].part_type -# Get the body of the second part -conversation.conversation_parts[1].body - -# REPLYING TO CONVERSATIONS -# User (identified by email) replies with a comment -conversation.reply( - type='user', email='joe@example.com', - message_type= comment', body='foo') -# Admin (identified by email) replies with a comment -conversation.reply( - type='admin', email='bob@example.com', - message_type='comment', body='bar') -# Admin (identified by id) opens a conversation -conversation.open_conversation(admin_id=7) -# Admin (identified by id) closes a conversation -conversation.close_conversation(admin_id=7) -# Admin (identified by id) assigns a conversation to an assignee -conversation.assign(assignee_id=8, admin_id=7) - -# MARKING A CONVERSATION AS READ -conversation.read = True -conversation.save() +asyncio.run(main()) ``` -#### Counts - -``` python -from intercom import Count -# Get Conversation per Admin -conversation_counts_for_each_admin = Count.conversation_counts_for_each_admin() -for count in conversation_counts_for_each_admin: - print "Admin: %s (id: %s) Open: %s Closed: %s" % ( - count.name, count.id, count.open, count.closed) -# Get User Tag Count Object -Count.user_counts_for_each_tag() -# Get User Segment Count Object -Count.user_counts_for_each_segment() -# Get Company Segment Count Object -Count.company_counts_for_each_segment() -# Get Company Tag Count Object -Count.company_counts_for_each_tag() -# Get Company User Count Object -Count.company_counts_for_each_user() -# Get total count of companies, users, segments or tags across app -Company.count() -User.count() -Segment.count() -Tag.count() -``` +## Exception Handling -#### Full loading of and embedded entity +When the API returns a non-success status code (4xx or 5xx response), a subclass of the following error +will be thrown. -``` python - # Given a converation with a partial user, load the full user. This can be done for any entity - conversation.user.load() +```python +from intercom.core.api_error import ApiError + +try: + client.ai_content.create_content_import_source(...) +except ApiError as e: + print(e.status_code) + print(e.body) ``` -#### Sending messages - -``` python -# InApp message from admin to user -Message.create(**{ - "message_type": "inapp", - "body": "What's up :)", - "from": { - "type": "admin", - "id": "1234" - }, - "to": { - "type": "user", - "id": "5678" - } -}) +## Pagination -# Email message from admin to user -Message.create(**{ - "message_type": "email", - "subject": "Hey there", - "body": "What's up :)", - "template": "plain", # or "personal", - "from": { - "type": "admin", - "id": "1234" - }, - "to": { - "type": "user", - "id": "536e564f316c83104c000020" - } -}) +Paginated requests will return a `SyncPager` or `AsyncPager`, which can be used as generators for the underlying object. -# Message from a user -Message.create(**{ - "from": { - "type": "user", - "id": "536e564f316c83104c000020" - }, - "body": "halp" -}) -``` +```python +from intercom import Intercom -#### Events - -``` python -from intercom import Event -Event.create( - event_name="invited-friend", - created_at=time.mktime(), - email=user.email, - metadata={ - "invitee_email": "pi@example.org", - "invite_code": "ADDAFRIEND", - "found_date": 12909364407 - } +client = Intercom( + token="YOUR_TOKEN", ) +response = client.articles.list() +for item in response: + yield item +# alternatively, you can paginate page-by-page +for page in response.iter_pages(): + yield page ``` -Metadata Objects support a few simple types that Intercom can present on your behalf - -``` python -Event.create( - event_name="placed-order", - email=current_user.email, - created_at=1403001013 - metadata={ - "order_date": time.mktime(), - "stripe_invoice": 'inv_3434343434', - "order_number": { - "value": '3434-3434', - "url": 'https://example.org/orders/3434-3434' - }, - "price": { - "currency": 'usd', - "amount": 2999 - } - } -) +```python +# You can also iterate through pages and access the typed response per page +pager = client.articles.list(...) +for page in pager.iter_pages(): + print(page.response) # access the typed response for each page + for item in page: + print(item) ``` -The metadata key values in the example are treated as follows- -- order_date: a Date (key ends with '_date'). -- stripe_invoice: The identifier of the Stripe invoice (has a 'stripe_invoice' key) -- order_number: a Rich Link (value contains 'url' and 'value' keys) -- price: An Amount in US Dollars (value contains 'amount' and 'currency' keys) - -### Subscriptions +## Advanced -Subscribe to events in Intercom to receive webhooks. +### Access Raw Response Data -``` python -from intercom import Subscription -# create a subscription -Subscription.create(url="http://example.com", topics=["user.created"]) +The SDK provides access to raw response data, including headers, through the `.with_raw_response` property. +The `.with_raw_response` property returns a "raw" client that can be used to access the `.headers` and `.data` attributes. -# fetch a subscription -Subscription.find(id="nsub_123456789") +```python +from intercom import Intercom -# list subscriptions -Subscription.all(): +client = Intercom( + ..., +) +response = client.ai_content.with_raw_response.create_content_import_source(...) +print(response.headers) # access the response headers +print(response.data) # access the underlying object +pager = client.articles.list(...) +print(pager.response) # access the typed response for the first page +for item in pager: + print(item) # access the underlying object(s) +for page in pager.iter_pages(): + print(page.response) # access the typed response for each page + for item in page: + print(item) # access the underlying object(s) ``` -### Webhooks +### Retries + +The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long +as the request is deemed retryable and the number of retry attempts has not grown larger than the configured +retry limit (default: 2). -``` python -from intercom import Notification -# create a payload from the notification hash (from json). -payload = Intercom::Notification.new(notification_hash) +A request is deemed retryable when any of the following HTTP status codes is returned: -payload.type -# 'user.created' +- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout) +- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests) +- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) (Internal Server Errors) -payload.model_type -# User +Use the `max_retries` request option to configure this behavior. -user = payload.model -# Instance of User +```python +client.ai_content.create_content_import_source(..., request_options={ + "max_retries": 1 +}) ``` -Note that models generated from webhook notifications might differ slightly from models directly acquired via the API. If this presents a problem, calling `payload.load` will load the model from the API using the `id` field. +### Timeouts +The SDK defaults to a 60 second timeout. You can configure this with a timeout option at the client or request level. -### Errors +```python -You do not need to deal with the HTTP response from an API call directly. If there is an unsuccessful response then an error that is a subclass of `intercom.Error` will be raised. If desired, you can get at the http_code of an `Error` via it's `http_code` method. +from intercom import Intercom -The list of different error subclasses are listed below. As they all inherit off `IntercomError` you can choose to except `IntercomError` or the more specific error subclass: +client = Intercom( + ..., + timeout=20.0, +) -```python -AuthenticationError -ServerError -ServiceUnavailableError -ResourceNotFound -BadGatewayError -BadRequestError -RateLimitExceeded -MultipleMatchingUsersError -HttpError -UnexpectedError + +# Override timeout for a specific method +client.ai_content.create_content_import_source(..., request_options={ + "timeout_in_seconds": 1 +}) ``` -### Rate Limiting +### Custom Client -Calling `Intercom.rate_limit_details` returns a dict that contains details about your app's current rate limit. +You can override the `httpx` client to customize it for your use-case. Some common use-cases include support for proxies +and transports. ```python -Intercom.rate_limit_details -# {'limit': 500, 'reset_at': datetime.datetime(2015, 3, 28, 13, 22), 'remaining': 497} +import httpx +from intercom import Intercom + +client = Intercom( + ..., + httpx_client=httpx.Client( + proxy="http://my.test.proxy.example.com", + transport=httpx.HTTPTransport(local_address="0.0.0.0"), + ), +) ``` -## Running the Tests - -Unit tests: +## Contributing -```bash -nosetests tests/unit -``` +While we value open-source contributions to this SDK, this library is generated programmatically. +Additions made directly to this library would have to be moved over to our generation code, +otherwise they would be overwritten upon the next generated release. Feel free to open a PR as +a proof of concept, but know that we will not be able to merge it as-is. We suggest opening +an issue first to discuss with us! -Integration tests: - -```bash -INTERCOM_APP_ID=xxx INTERCOM_APP_API_KEY=xxx nosetests tests/integration -``` +On the other hand, contributions to the README are always very welcome! diff --git a/README.rst b/README.rst deleted file mode 100644 index 82fc989c..00000000 --- a/README.rst +++ /dev/null @@ -1,486 +0,0 @@ -python-intercom -=============== - -|PyPI Version| |PyPI Downloads| |Travis CI Build| |Coverage Status| - -Python bindings for the Intercom API (https://api.intercom.io). - -`API Documentation `__. - -`Package -Documentation `__. - -Upgrading information ---------------------- - -Version 2 of python-intercom is **not backwards compatible** with -previous versions. - -One change you will need to make as part of the upgrade is to set -``Intercom.app_api_key`` and not set ``Intercom.api_key``. - -Installation ------------- - -:: - - pip install python-intercom - -Basic Usage ------------ - -Configure your access credentials -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: python - - Intercom.app_id = "my_app_id" - Intercom.app_api_key = "my-super-crazy-api-key" - -Resources -~~~~~~~~~ - -Resources this API supports: - -:: - - https://api.intercom.io/users - https://api.intercom.io/companies - https://api.intercom.io/tags - https://api.intercom.io/notes - https://api.intercom.io/segments - https://api.intercom.io/events - https://api.intercom.io/conversations - https://api.intercom.io/messages - https://api.intercom.io/counts - https://api.intercom.io/subscriptions - -Additionally, the library can handle incoming webhooks from Intercom and -convert to ``intercom`` models. - -Examples -~~~~~~~~ - -Users -^^^^^ - -.. code:: python - - from intercom import User - # Find user by email - user = User.find(email="bob@example.com") - # Find user by user_id - user = User.find(user_id="1") - # Find user by id - user = User.find(id="1") - # Create a user - user = User.create(email="bob@example.com", name="Bob Smith") - # Delete a user - deleted_user = User.find(id="1").delete() - # Update custom_attributes for a user - user.custom_attributes["average_monthly_spend"] = 1234.56 - user.save() - # Perform incrementing - user.increment('karma') - user.save() - # Iterate over all users - for user in User.all(): - ... - -Admins -^^^^^^ - -.. code:: python - - from intercom import Admin - # Iterate over all admins - for admin in Admin.all(): - ... - -Companies -^^^^^^^^^ - -.. code:: python - - from intercom import Company - from intercom import User - # Add a user to one or more companies - user = User.find(email="bob@example.com") - user.companies = [ - {"company_id": 6, "name": "Intercom"}, - {"company_id": 9, "name": "Test Company"} - ] - user.save() - # You can also pass custom attributes within a company as you do this - user.companies = [ - { - "id": 6, - "name": "Intercom", - "custom_attributes": { - "referral_source": "Google" - } - } - ] - user.save() - # Find a company by company_id - company = Company.find(company_id="44") - # Find a company by name - company = Company.find(name="Some company") - # Find a company by id - company = Company.find(id="41e66f0313708347cb0000d0") - # Update a company - company.name = 'Updated company name' - company.save() - # Iterate over all companies - for company in Company.all(): - ... - # Get a list of users in a company - company.users - -Tags -^^^^ - -.. code:: python - - from intercom import Tag - # Tag users - tag = Tag.tag_users('blue', ["42ea2f1b93891f6a99000427"]) - # Untag users - Tag.untag_users('blue', ["42ea2f1b93891f6a99000427"]) - # Iterate over all tags - for tag in Tag.all(): - ... - # Iterate over all tags for user - Tag.find_all_for_user(id='53357ddc3c776629e0000029') - Tag.find_all_for_user(email='declan+declan@intercom.io') - Tag.find_all_for_user(user_id='3') - # Tag companies - tag = Tag.tag_companies('red', ["42ea2f1b93891f6a99000427"]) - # Untag companies - Tag.untag_companies('blue', ["42ea2f1b93891f6a99000427"]) - # Iterate over all tags for company - Tag.find_all_for_company(id='43357e2c3c77661e25000026') - Tag.find_all_for_company(company_id='6') - -Segments -^^^^^^^^ - -.. code:: python - - from intercom import Segment - # Find a segment - segment = Segment.find(id=segment_id) - # Update a segment - segment.name = 'Updated name' - segment.save() - # Iterate over all segments - for segment in Segment.all(): - ... - -Notes -^^^^^ - -.. code:: python - - # Find a note by id - note = Note.find(id=note) - # Create a note for a user - note = Note.create( - body="

Text for the note

", - email='joe@example.com') - # Iterate over all notes for a user via their email address - for note in Note.find_all(email='joe@example.com'): - ... - # Iterate over all notes for a user via their user_id - for note in Note.find_all(user_id='123'): - ... - -Conversations -^^^^^^^^^^^^^ - -.. code:: python - - from intercom import Conversation - # FINDING CONVERSATIONS FOR AN ADMIN - # Iterate over all conversations (open and closed) assigned to an admin - for convo in Conversation.find_all(type='admin', id='7'): - ... - # Iterate over all open conversations assigned to an admin - for convo Conversation.find_all(type='admin', id=7, open=True): - ... - # Iterate over closed conversations assigned to an admin - for convo Conversation.find_all(type='admin', id=7, open=False): - ... - # Iterate over closed conversations for assigned an admin, before a certain - # moment in time - for convo in Conversation.find_all( - type='admin', id= 7, open= False, before=1374844930): - ... - - # FINDING CONVERSATIONS FOR A USER - # Iterate over all conversations (read + unread, correct) with a user based on - # the users email - for convo in Conversation.find_all(email='joe@example.com',type='user'): - ... - # Iterate over through all conversations (read + unread) with a user based on - # the users email - for convo in Conversation.find_all( - email='joe@example.com', type='user', unread=False): - ... - # Iterate over all unread conversations with a user based on the users email - for convo in Conversation.find_all( - email='joe@example.com', type='user', unread=true): - ... - - # FINDING A SINGLE CONVERSATION - conversation = Conversation.find(id='1') - - # INTERACTING WITH THE PARTS OF A CONVERSATION - # Getting the subject of a part (only applies to email-based conversations) - conversation.rendered_message.subject - # Get the part_type of the first part - conversation.conversation_parts[0].part_type - # Get the body of the second part - conversation.conversation_parts[1].body - - # REPLYING TO CONVERSATIONS - # User (identified by email) replies with a comment - conversation.reply( - type='user', email='joe@example.com', - message_type= comment', body='foo') - # Admin (identified by email) replies with a comment - conversation.reply( - type='admin', email='bob@example.com', - message_type='comment', body='bar') - - # MARKING A CONVERSATION AS READ - conversation.read = True - conversation.save() - -Counts -^^^^^^ - -.. code:: python - - from intercom import Count - # Get Conversation per Admin - conversation_counts_for_each_admin = Count.conversation_counts_for_each_admin() - for count in conversation_counts_for_each_admin: - print "Admin: %s (id: %s) Open: %s Closed: %s" % ( - count.name, count.id, count.open, count.closed) - # Get User Tag Count Object - Count.user_counts_for_each_tag() - # Get User Segment Count Object - Count.user_counts_for_each_segment() - # Get Company Segment Count Object - Count.company_counts_for_each_segment() - # Get Company Tag Count Object - Count.company_counts_for_each_tag() - # Get Company User Count Object - Count.company_counts_for_each_user() - # Get total count of companies, users, segments or tags across app - Company.count() - User.count() - Segment.count() - Tag.count() - -Full loading of and embedded entity -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: python - - # Given a converation with a partial user, load the full user. This can be done for any entity - conversation.user.load() - -Sending messages -^^^^^^^^^^^^^^^^ - -.. code:: python - - # InApp message from admin to user - Message.create(**{ - "message_type": "inapp", - "body": "What's up :)", - "from": { - "type": "admin", - "id": "1234" - }, - "to": { - "type": "user", - "id": "5678" - } - }) - - # Email message from admin to user - Message.create(**{ - "message_type": "email", - "subject": "Hey there", - "body": "What's up :)", - "template": "plain", # or "personal", - "from": { - "type": "admin", - "id": "1234" - }, - "to": { - "type": "user", - "id": "536e564f316c83104c000020" - } - }) - - # Message from a user - Message.create(**{ - "from": { - "type": "user", - "id": "536e564f316c83104c000020" - }, - "body": "halp" - }) - -Events -^^^^^^ - -.. code:: python - - from intercom import Event - Event.create( - event_name="invited-friend", - created_at=time.mktime(), - email=user.email, - metadata={ - "invitee_email": "pi@example.org", - "invite_code": "ADDAFRIEND", - "found_date": 12909364407 - } - ) - -Metadata Objects support a few simple types that Intercom can present on -your behalf - -.. code:: python - - Event.create( - event_name="placed-order", - email=current_user.email, - created_at=1403001013 - metadata={ - "order_date": time.mktime(), - "stripe_invoice": 'inv_3434343434', - "order_number": { - "value": '3434-3434', - "url": 'https://example.org/orders/3434-3434' - }, - "price": { - "currency": 'usd', - "amount": 2999 - } - } - ) - -The metadata key values in the example are treated as follows- - -- order\_date: a Date (key ends with '\_date'). -- stripe\_invoice: The identifier of the Stripe invoice (has a - 'stripe\_invoice' key) -- order\_number: a Rich Link (value contains 'url' and 'value' keys) -- price: An Amount in US Dollars (value contains 'amount' and - 'currency' keys) - -Subscriptions -~~~~~~~~~~~~~ - -Subscribe to events in Intercom to receive webhooks. - -.. code:: python - - from intercom import Subscription - # create a subscription - Subscription.create(url="http://example.com", topics=["user.created"]) - - # fetch a subscription - Subscription.find(id="nsub_123456789") - - # list subscriptions - Subscription.all(): - -Webhooks -~~~~~~~~ - -.. code:: python - - from intercom import Notification - # create a payload from the notification hash (from json). - payload = Intercom::Notification.new(notification_hash) - - payload.type - # 'user.created' - - payload.model_type - # User - - user = payload.model - # Instance of User - -Note that models generated from webhook notifications might differ -slightly from models directly acquired via the API. If this presents a -problem, calling ``payload.load`` will load the model from the API using -the ``id`` field. - -Errors -~~~~~~ - -You do not need to deal with the HTTP response from an API call -directly. If there is an unsuccessful response then an error that is a -subclass of ``intercom.Error`` will be raised. If desired, you can get -at the http\_code of an ``Error`` via it's ``http_code`` method. - -The list of different error subclasses are listed below. As they all -inherit off ``IntercomError`` you can choose to except ``IntercomError`` -or the more specific error subclass: - -.. code:: python - - AuthenticationError - ServerError - ServiceUnavailableError - ResourceNotFound - BadGatewayError - BadRequestError - RateLimitExceeded - MultipleMatchingUsersError - HttpError - UnexpectedError - -Rate Limiting -~~~~~~~~~~~~~ - -Calling ``Intercom.rate_limit_details`` returns a dict that contains -details about your app's current rate limit. - -.. code:: python - - Intercom.rate_limit_details - # {'limit': 500, 'reset_at': datetime.datetime(2015, 3, 28, 13, 22), 'remaining': 497} - -Running the Tests ------------------ - -Unit tests: - -.. code:: bash - - nosetests tests/unit - -Integration tests: - -.. code:: bash - - INTERCOM_APP_ID=xxx INTERCOM_APP_API_KEY=xxx nosetests tests/integration - -.. |PyPI Version| image:: https://img.shields.io/pypi/v/python-intercom.svg - :target: https://pypi.python.org/pypi/python-intercom -.. |PyPI Downloads| image:: https://img.shields.io/pypi/dm/python-intercom.svg - :target: https://pypi.python.org/pypi/python-intercom -.. |Travis CI Build| image:: https://travis-ci.org/jkeyes/python-intercom.svg - :target: https://travis-ci.org/jkeyes/python-intercom -.. |Coverage Status| image:: https://coveralls.io/repos/jkeyes/python-intercom/badge.svg?branch=master - :target: https://coveralls.io/r/jkeyes/python-intercom?branch=master diff --git a/dev-requirements.txt b/dev-requirements.txt deleted file mode 100644 index 906b0520..00000000 --- a/dev-requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -# -# Development dependencies. -# -nose==1.3.4 -mock==1.0.1 -coveralls==0.5 -coverage==3.7.1 \ No newline at end of file diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index e1c42828..00000000 --- a/docs/Makefile +++ /dev/null @@ -1,153 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/intercom.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/intercom.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/intercom" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/intercom" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/docs/api/intercom.api_operations.rst b/docs/api/intercom.api_operations.rst deleted file mode 100644 index a78ca151..00000000 --- a/docs/api/intercom.api_operations.rst +++ /dev/null @@ -1,70 +0,0 @@ -intercom.api_operations package -=============================== - -Submodules ----------- - -intercom.api_operations.all module ----------------------------------- - -.. automodule:: intercom.api_operations.all - :members: - :undoc-members: - :show-inheritance: - -intercom.api_operations.count module ------------------------------------- - -.. automodule:: intercom.api_operations.count - :members: - :undoc-members: - :show-inheritance: - -intercom.api_operations.delete module -------------------------------------- - -.. automodule:: intercom.api_operations.delete - :members: - :undoc-members: - :show-inheritance: - -intercom.api_operations.find module ------------------------------------ - -.. automodule:: intercom.api_operations.find - :members: - :undoc-members: - :show-inheritance: - -intercom.api_operations.find_all module ---------------------------------------- - -.. automodule:: intercom.api_operations.find_all - :members: - :undoc-members: - :show-inheritance: - -intercom.api_operations.load module ------------------------------------ - -.. automodule:: intercom.api_operations.load - :members: - :undoc-members: - :show-inheritance: - -intercom.api_operations.save module ------------------------------------ - -.. automodule:: intercom.api_operations.save - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: intercom.api_operations - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/api/intercom.extended_api_operations.rst b/docs/api/intercom.extended_api_operations.rst deleted file mode 100644 index c35ef2e6..00000000 --- a/docs/api/intercom.extended_api_operations.rst +++ /dev/null @@ -1,30 +0,0 @@ -intercom.extended_api_operations package -======================================== - -Submodules ----------- - -intercom.extended_api_operations.reply module ---------------------------------------------- - -.. automodule:: intercom.extended_api_operations.reply - :members: - :undoc-members: - :show-inheritance: - -intercom.extended_api_operations.users module ---------------------------------------------- - -.. automodule:: intercom.extended_api_operations.users - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: intercom.extended_api_operations - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/api/intercom.generic_handlers.rst b/docs/api/intercom.generic_handlers.rst deleted file mode 100644 index 2266a494..00000000 --- a/docs/api/intercom.generic_handlers.rst +++ /dev/null @@ -1,46 +0,0 @@ -intercom.generic_handlers package -================================= - -Submodules ----------- - -intercom.generic_handlers.base_handler module ---------------------------------------------- - -.. automodule:: intercom.generic_handlers.base_handler - :members: - :undoc-members: - :show-inheritance: - -intercom.generic_handlers.count module --------------------------------------- - -.. automodule:: intercom.generic_handlers.count - :members: - :undoc-members: - :show-inheritance: - -intercom.generic_handlers.tag module ------------------------------------- - -.. automodule:: intercom.generic_handlers.tag - :members: - :undoc-members: - :show-inheritance: - -intercom.generic_handlers.tag_find_all module ---------------------------------------------- - -.. automodule:: intercom.generic_handlers.tag_find_all - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: intercom.generic_handlers - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/api/intercom.lib.rst b/docs/api/intercom.lib.rst deleted file mode 100644 index 405ad2dd..00000000 --- a/docs/api/intercom.lib.rst +++ /dev/null @@ -1,38 +0,0 @@ -intercom.lib package -==================== - -Submodules ----------- - -intercom.lib.flat_store module ------------------------------- - -.. automodule:: intercom.lib.flat_store - :members: - :undoc-members: - :show-inheritance: - -intercom.lib.setter_property module ------------------------------------ - -.. automodule:: intercom.lib.setter_property - :members: - :undoc-members: - :show-inheritance: - -intercom.lib.typed_json_deserializer module -------------------------------------------- - -.. automodule:: intercom.lib.typed_json_deserializer - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: intercom.lib - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/api/intercom.rst b/docs/api/intercom.rst deleted file mode 100644 index 0eb19ded..00000000 --- a/docs/api/intercom.rst +++ /dev/null @@ -1,153 +0,0 @@ -intercom package -================ - -Subpackages ------------ - -.. toctree:: - - intercom.api_operations - intercom.extended_api_operations - intercom.generic_handlers - intercom.lib - intercom.traits - -Submodules ----------- - -intercom.admin module ---------------------- - -.. automodule:: intercom.admin - :members: - :undoc-members: - :show-inheritance: - -intercom.collection_proxy module --------------------------------- - -.. automodule:: intercom.collection_proxy - :members: - :undoc-members: - :show-inheritance: - -intercom.company module ------------------------ - -.. automodule:: intercom.company - :members: - :undoc-members: - :show-inheritance: - -intercom.conversation module ----------------------------- - -.. automodule:: intercom.conversation - :members: - :undoc-members: - :show-inheritance: - -intercom.count module ---------------------- - -.. automodule:: intercom.count - :members: - :undoc-members: - :show-inheritance: - -intercom.errors module ----------------------- - -.. automodule:: intercom.errors - :members: - :undoc-members: - :show-inheritance: - -intercom.event module ---------------------- - -.. automodule:: intercom.event - :members: - :undoc-members: - :show-inheritance: - -intercom.message module ------------------------ - -.. automodule:: intercom.message - :members: - :undoc-members: - :show-inheritance: - -intercom.note module --------------------- - -.. automodule:: intercom.note - :members: - :undoc-members: - :show-inheritance: - -intercom.notification module ----------------------------- - -.. automodule:: intercom.notification - :members: - :undoc-members: - :show-inheritance: - -intercom.request module ------------------------ - -.. automodule:: intercom.request - :members: - :undoc-members: - :show-inheritance: - -intercom.segment module ------------------------ - -.. automodule:: intercom.segment - :members: - :undoc-members: - :show-inheritance: - -intercom.subscription module ----------------------------- - -.. automodule:: intercom.subscription - :members: - :undoc-members: - :show-inheritance: - -intercom.tag module -------------------- - -.. automodule:: intercom.tag - :members: - :undoc-members: - :show-inheritance: - -intercom.user module --------------------- - -.. automodule:: intercom.user - :members: - :undoc-members: - :show-inheritance: - -intercom.utils module ---------------------- - -.. automodule:: intercom.utils - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: intercom - :members: - :undoc-members: - :show-inheritance: \ No newline at end of file diff --git a/docs/api/intercom.traits.rst b/docs/api/intercom.traits.rst deleted file mode 100644 index d20886f2..00000000 --- a/docs/api/intercom.traits.rst +++ /dev/null @@ -1,30 +0,0 @@ -intercom.traits package -======================= - -Submodules ----------- - -intercom.traits.api_resource module ------------------------------------ - -.. automodule:: intercom.traits.api_resource - :members: - :undoc-members: - :show-inheritance: - -intercom.traits.incrementable_attributes module ------------------------------------------------ - -.. automodule:: intercom.traits.incrementable_attributes - :members: - :undoc-members: - :show-inheritance: - - -Module contents ---------------- - -.. automodule:: intercom.traits - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/api/modules.rst b/docs/api/modules.rst deleted file mode 100644 index bbeffb0c..00000000 --- a/docs/api/modules.rst +++ /dev/null @@ -1,7 +0,0 @@ -intercom -======== - -.. toctree:: - :maxdepth: 4 - - intercom diff --git a/docs/changelog.rst b/docs/changelog.rst deleted file mode 100644 index d9e113ec..00000000 --- a/docs/changelog.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../CHANGES.rst diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index bd82ab30..00000000 --- a/docs/conf.py +++ /dev/null @@ -1,259 +0,0 @@ -# -*- coding: utf-8 -*- -# -# intercom documentation build configuration file, created by -# sphinx-quickstart on Tue Mar 20 09:38:03 2012. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sphinx_rtd_theme -import sys, os - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) -docs_dir = os.path.dirname(__file__) -path_dir = os.path.abspath(os.path.join(docs_dir, '..')) -sys.path.insert(0, path_dir) - -# -- General configuration ----------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'python-intercom' -from datetime import datetime -now = datetime.now() -copyright = u'%s, John Keyes' % (now.year) - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. - -import re -with open(os.path.join(path_dir, 'intercom', '__init__.py')) as init: - source = init.read() - m = re.search("__version__ = '(\d+\.\d+(\.(\d+|[a-z]+))?)'", source, re.M) - version = m.groups()[0] - -# The full version, including alpha/beta/rc tags. -release = version - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['_build'] - -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - - -# -- Options for HTML output --------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -try: - import sphinx_rtd_theme - html_theme = "sphinx_rtd_theme" - html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] -except ImportError: - html_theme = 'default' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'intercomdoc' - - -# -- Options for LaTeX output -------------------------------------------------- - -latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [ - ('index', 'python-intercom.tex', u'python-intercom Documentation', - u'John Keyes', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output -------------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'python-intercom', u'python-intercom Documentation', - [u'John Keyes'], 1) -] - -# If true, show URL addresses after external links. -#man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------------ - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'python-intercom', u'python-intercom Documentation', - u'John Keyes', 'python-intercom', 'One line description of project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' diff --git a/docs/development.rst b/docs/development.rst deleted file mode 100644 index 21f2256c..00000000 --- a/docs/development.rst +++ /dev/null @@ -1,58 +0,0 @@ -Development -=========== - -Running the tests ------------------ - -Run the unit tests: - -:: - - nosetests tests/unit - -Run the integration tests: - -:: - - # THESE SHOULD ONLY BE RUN ON A TEST APP! - INTERCOM_APP_ID=xxx INTERCOM_APP_API_KEY=xxx nosetests tests/integration - -Generate the Documentation --------------------------- - -:: - - cd docs - make html - -Code coverage -------------- - -Generate a code coverage report: - -:: - - nosetests --with-coverage --cover-package=intercom tests/unit - - -Runtime Dependencies --------------------- - -* `Requests `_ – an HTTP library “for human beings” -* `inflection `_ – Inflection is a string transformation library. It singularizes and pluralizes English words, and transforms strings from CamelCase to underscored string. -* `six `_ – Six is a Python 2 and 3 compatibility library. It provides utility functions for smoothing over the differences between the Python versions with the goal of writing Python code that is compatible on both Python versions. -* `certifi `_ – Certifi is a carefully curated collection of Root Certificates for validating the trustworthiness of SSL certificates while verifying the identity of TLS hosts. - -Development Dependencies ------------------------- - -* `nose `_ – makes unit testing easier. -* `coverage `_ – code coverage. -* `mock `_ – patching methods for unit testing. -* `Sphinx `_ – documentation decorator. - - -Authors -------- - -.. include:: ../AUTHORS.rst diff --git a/docs/faq.rst b/docs/faq.rst deleted file mode 100644 index bb1b66a8..00000000 --- a/docs/faq.rst +++ /dev/null @@ -1,12 +0,0 @@ -FAQ -=== - -How do I start a session? -------------------------- - -:: - - user = User.create(email='bingo@example.com') - # register a new session - user.new_session = True - user.save() diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index 7f20b237..00000000 --- a/docs/index.rst +++ /dev/null @@ -1,478 +0,0 @@ -=============== -python-intercom -=============== - -.. toctree:: - :hidden: - - installation - faq - changelog - development - api/modules - -Installation -============ - -Stable releases of python-intercom can be installed with -`pip `_ or you may download a `.tgz` source -archive from `pypi `_. -See the :doc:`installation` page for more detailed instructions. - -If you want to use the latest code, you can grab it from our -`Git repository `_, or `fork it `_. - -Usage -=================================== - -Authentication ---------------- - -Intercom documentation: `Basic Authorization `_. - -:: - - from intercom import Intercom - Intercom.app_id = 'dummy-app-id' - Intercom.app_api_key = 'dummy-api-key' - -Users ------ - -Create or Update User -+++++++++++++++++++++ - -Intercom documentation: `Create or Update Users `_. - -:: - - from intercom import User - User.create(id='1234', email='bob@example.com') - -Updating the Last Seen Time -+++++++++++++++++++++++++++ - -Intercom documentation: `Updating the Last Seen Time `_. - -:: - - user = User.create(used_id='25', last_request_at=datetime.now()) - -List Users -++++++++++ - -Intercom documentation: `List Users `_. - -:: - - for user in User.all(): - ... - -List by Tag, Segment, Company -+++++++++++++++++++++++++++++ - -Intercom documentation: `List by Tag, Segment, Company `_. - -:: - - # tag request - User.find_all(tag_id='30126') - - # segment request - User.find_all(segment_id='30126') - - -View a User -+++++++++++ - -Intercom documentation: `View a User `_. - -:: - - # ID request - User.find(id='1') - - # User ID request - User.find(user_id='1') - - # Email request - User.find(email='bob@example.com') - -Delete a User -+++++++++++++ - -Intercom documentation: `Deleting a User `_. - -:: - - # ID Delete Request - deleted_user = User.find(id='1').delete() - - # User ID Delete Request - deleted_user = User.find(user_id='1').delete() - - # Email Delete Request - deleted_user = User.find(email='bob@example.com').delete() - - -Companies ---------- - -Create or Update Company -++++++++++++++++++++++++ - -Intercom documentation: `Create or Update Company `_. - -:: - - Company.create(company_id=6, name="Blue Sun", plan="Paid") - -List Companies -++++++++++++++ - -Intercom documentation: `List Companies `_. - -:: - - for company in Company.all(): - ... - -List by Tag or Segment -++++++++++++++++++++++ - -Intercom documentation: `List by Tag or Segment `_. - -:: - - # tag request - Company.find(tag_id="1234") - - # segment request - Company.find(segment_id="4567") - -View a Company -++++++++++++++ - -Intercom documentation: `View a Company `_. - -:: - - Company.find(id="41e66f0313708347cb0000d0") - -List Company Users -++++++++++++++++++ - -Intercom documentation: `List Company Users `_. - -:: - - company = Company.find(id="41e66f0313708347cb0000d0") - for user in company.users: - ... - -Admins ------- - -List Admins -+++++++++++ - -Intercom documentation: `List Admins `_. - -:: - - from intercom import Admin - for admin in Admin.all(): - ... - -Tags ----- - -Create and Update Tags -++++++++++++++++++++++ - -Intercom documentation: `Create and Update Tags `_. - -:: - - from intercom import Tag - - # Create Request - tag = Tag.create(name='Independentt') - - # Update Request - Tag.tag_users(name='Independent', id=tag.id) - - -Tag or Untag Users & Companies -++++++++++++++++++++++++++++++ - -Intercom documentation: `Tag or Untag Users & Companies `_. - -:: - - # Multi-User Tag Request - Tag.tag_users('Independent', ["42ea2f1b93891f6a99000427", "42ea2f1b93891f6a99000428"]) - - # Untag Request - Tag.untag_users('blue', ["42ea2f1b93891f6a99000427"]) - -Delete a Tag -++++++++++++ - -Intercom documentation: `Delete a Tag `_. - -:: - - tag.delete() - - -List Tags for an App -++++++++++++++++++++ - -Intercom Documentation: `List Tags for an App `_. - -:: - - for tag in Tag.all(): - ... - -Segments --------- - -List Segments -+++++++++++++ - -Intercom Documentation: `List Segments `_. - -:: - - from intercom import Segment - - for segment in Segment.all(): - ... - -View a Segment -++++++++++++++ - -Intercom Documentation: `View a Segment `_. - -:: - - Segment.find(id='1234') - -Notes ------ - -Create a Note -+++++++++++++ - -Intercom documentation: `Create a Note `_. - -:: - - from intercom import Note - - Note.create(email="joe@exampe.com", body="Text for the note") - - -List Notes for a User -+++++++++++++++++++++ - -Intercom documentation: `List Notes for a User `_. - -:: - - # User ID Request - for note in Note.find_all(user_id='123'): - ... - - # User Email Request - for note in Note.find_all(email='foo@bar.com'): - ... - -View a Note -+++++++++++ - -Intercom documentation: `View a Note `_. - -:: - - Note.find(id='1234') - -Events ------- - -Submitting Events -+++++++++++++++++ - -Intercom documentation: `Submitting Events `_. - -:: - - from intercom import Event - Event.create(event_name="Eventful 1", email=user.email, created_at=1403001013) - - -Counts ------- - -Getting counts -++++++++++++++ - -Intercom documentation: `Creating a Tag `_. - -:: - - from intercom import Count - - # Conversation Admin Count - Count.conversation_counts_for_each_admin - - # User Tag Count - Count.user_counts_for_each_tag - - # User Segment Count - Count.user_counts_for_each_segment - - # Company Segment Count - Count.company_counts_for_each_segment - - # Company Tag Count - Count.company_counts_for_each_tag - - # Company User Count - Count.company_counts_for_each_user - - # Global App Counts - Company.count - User.count - Segment.count - Tag.count - -Conversations -------------- - -Admin Initiated Conversation -++++++++++++++++++++++++++++ - -Intercom documentation: `Admin Initiated Conversation `_. - -:: - - from intercom import Message - message_data = { - 'message_type': 'email', - 'subject': 'This Land', - 'body': "Har har har! Mine is an evil laugh!", - 'template': "plain", - 'from': { - 'type': "admin", - 'id': "394051" - }, - 'to': { - 'type': "user", - 'id': "536e564f316c83104c000020" - } - } - Message.create(**message_data) - -User Initiated Conversation -+++++++++++++++++++++++++++ - -Intercom documentation: `User Initiated Conversation `_. - -:: - - message_data = { - 'from': { - 'type': "user", - 'id': "536e564f316c83104c000020" - }, - 'body': "Hey" - } - Message.create(**message_data) - -List Conversations -++++++++++++++++++ - -Intercom documentation: `List Conversations `_. - -:: - - from intercom import Conversation - Conversation.find_all(type='admin', id=25, open=True) - - -Get a Single Conversation -+++++++++++++++++++++++++ - -Intercom documentation: `Get a Single Conversation `_. - -:: - - Conversation.find(id='147') - -Replying to a Conversation -++++++++++++++++++++++++++ - -Intercom documentation: `Replying to a Conversation `_. - -:: - - conversation.reply(type='user', email='bob@example.com', message_type='comment', body='foo') - - -Marking a Conversation as Read -++++++++++++++++++++++++++++++ - -Intercom documentation: `Marking a Conversation as Read `_. - -:: - - conversation.read = True - conversation.save() - - -Webhooks and Notifications --------------------------- - -Manage Subscriptions -++++++++++++++++++++ - -Intercom documentation: `Manage Subscriptions `_. - -:: - - from intercom import Subscription - Subscription.create(service_type='web', url='http://example.com', topics=['all']) - - -View a Subscription -+++++++++++++++++++ - -Intercom documentation: `View a Subscription `_. - -:: - - Subscription.find(id='123') - -List Subscriptions -++++++++++++++++++ - -Intercom documentation: `List Subscriptions `_. - -:: - - for subscription in Subscription.all(): - ... - -Development -=========== - -Our :doc:`development` page has detailed instructions on how to run our -tests, and to produce coverage and pylint reports. - -Changelog -========= - -The :doc:`changelog` keeps track of changes per release. diff --git a/docs/installation.rst b/docs/installation.rst deleted file mode 100644 index a23a75e3..00000000 --- a/docs/installation.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../INSTALL.rst diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index aefa4512..00000000 --- a/docs/make.bat +++ /dev/null @@ -1,190 +0,0 @@ -@ECHO OFF - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set BUILDDIR=_build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . -set I18NSPHINXOPTS=%SPHINXOPTS% . -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% - set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% -) - -if "%1" == "" goto help - -if "%1" == "help" ( - :help - echo.Please use `make ^` where ^ is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. devhelp to make HTML files and a Devhelp project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. text to make text files - echo. man to make manual pages - echo. texinfo to make Texinfo files - echo. gettext to make PO message catalogs - echo. changes to make an overview over all changed/added/deprecated items - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\intercom.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\intercom.ghc - goto end -) - -if "%1" == "devhelp" ( - %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "text" ( - %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The text files are in %BUILDDIR%/text. - goto end -) - -if "%1" == "man" ( - %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The manual pages are in %BUILDDIR%/man. - goto end -) - -if "%1" == "texinfo" ( - %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. - goto end -) - -if "%1" == "gettext" ( - %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The message catalogs are in %BUILDDIR%/locale. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - if errorlevel 1 exit /b 1 - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - if errorlevel 1 exit /b 1 - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - if errorlevel 1 exit /b 1 - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -:end diff --git a/intercom/__init__.py b/intercom/__init__.py deleted file mode 100644 index c7ef2e61..00000000 --- a/intercom/__init__.py +++ /dev/null @@ -1,179 +0,0 @@ -# -*- coding: utf-8 -*- - -from datetime import datetime -from .errors import (ArgumentError, AuthenticationError, # noqa - BadGatewayError, BadRequestError, HttpError, IntercomError, - MultipleMatchingUsersError, RateLimitExceeded, ResourceNotFound, - ServerError, ServiceUnavailableError, UnexpectedError) -from .lib.setter_property import SetterProperty -from .request import Request -from .admin import Admin # noqa -from .company import Company # noqa -from .count import Count # noqa -from .conversation import Conversation # noqa -from .event import Event # noqa -from .message import Message # noqa -from .note import Note # noqa -from .notification import Notification # noqa -from .user import User # noqa -from .segment import Segment # noqa -from .subscription import Subscription # noqa -from .tag import Tag # noqa - -import copy -import random -import re -import six -import time - -__version__ = '2.1.1' - - -RELATED_DOCS_TEXT = "See https://github.com/jkeyes/python-intercom \ -for usage examples." -COMPATIBILITY_WARNING_TEXT = "It looks like you are upgrading from \ -an older version of python-intercom. Please note that this new version \ -(%s) is not backwards compatible." % (__version__) -COMPATIBILITY_WORKAROUND_TEXT = "To get rid of this error please set \ -Intercom.app_api_key and don't set Intercom.api_key." -CONFIGURATION_REQUIRED_TEXT = "You must set both Intercom.app_id and \ -Intercom.app_api_key to use this client." - - -class IntercomType(type): # noqa - - app_id = None - app_api_key = None - _hostname = "api.intercom.io" - _protocol = "https" - _endpoints = None - _current_endpoint = None - _target_base_url = None - _endpoint_randomized_at = 0 - _rate_limit_details = {} - - @property - def _auth(self): - return (self.app_id, self.app_api_key) - - @property - def _random_endpoint(self): - if self.endpoints: - endpoints = copy.copy(self.endpoints) - random.shuffle(endpoints) - return endpoints[0] - - @property - def _alternative_random_endpoint(self): - endpoints = copy.copy(self.endpoints) - if self.current_endpoint in endpoints: - endpoints.remove(self.current_endpoint) - random.shuffle(endpoints) - if endpoints: - return endpoints[0] - - @property - def target_base_url(self): - if None in [self.app_id, self.app_api_key]: - raise ArgumentError('%s %s' % ( - CONFIGURATION_REQUIRED_TEXT, RELATED_DOCS_TEXT)) - if self._target_base_url is None: - basic_auth_part = '%s:%s@' % (self.app_id, self.app_api_key) - if self.current_endpoint: - self._target_base_url = re.sub( - r'(https?:\/\/)(.*)', - '\g<1>%s\g<2>' % (basic_auth_part), - self.current_endpoint) - return self._target_base_url - - @property - def hostname(self): - return self._hostname - - @hostname.setter - def hostname(self, value): - self._hostname = value - self.current_endpoint = None - self.endpoints = None - - @property - def rate_limit_details(self): - return self._rate_limit_details - - @rate_limit_details.setter - def rate_limit_details(self, value): - self._rate_limit_details = value - - @property - def protocol(self): - return self._protocol - - @protocol.setter - def protocol(self, value): - self._protocol = value - self.current_endpoint = None - self.endpoints = None - - @property - def current_endpoint(self): - now = time.mktime(datetime.utcnow().timetuple()) - expired = self._endpoint_randomized_at < (now - (60 * 5)) - if self._endpoint_randomized_at is None or expired: - self._endpoint_randomized_at = now - self._current_endpoint = self._random_endpoint - return self._current_endpoint - - @current_endpoint.setter - def current_endpoint(self, value): - self._current_endpoint = value - self._target_base_url = None - - @property - def endpoints(self): - if not self._endpoints: - return ['%s://%s' % (self.protocol, self.hostname)] - else: - return self._endpoints - - @endpoints.setter - def endpoints(self, value): - self._endpoints = value - self.current_endpoint = self._random_endpoint - - @SetterProperty - def endpoint(self, value): - self.endpoints = [value] - - -@six.add_metaclass(IntercomType) -class Intercom(object): - _class_register = {} - - @classmethod - def get_url(cls, path): - if '://' in path: - url = path - else: - url = cls.current_endpoint + path - return url - - @classmethod - def request(cls, method, path, params): - return Request.send_request_to_path( - method, cls.get_url(path), cls._auth, params) - - @classmethod - def get(cls, path, **params): - return cls.request('GET', path, params) - - @classmethod - def post(cls, path, **params): - return cls.request('POST', path, params) - - @classmethod - def put(cls, path, **params): - return cls.request('PUT', path, params) - - @classmethod - def delete(cls, path, **params): - return cls.request('DELETE', path, params) diff --git a/intercom/admin.py b/intercom/admin.py deleted file mode 100644 index 646838e2..00000000 --- a/intercom/admin.py +++ /dev/null @@ -1,9 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom.api_operations.all import All -from intercom.api_operations.find import Find -from intercom.traits.api_resource import Resource - - -class Admin(Resource, Find, All): - pass diff --git a/intercom/api_operations/__init__.py b/intercom/api_operations/__init__.py deleted file mode 100644 index 40a96afc..00000000 --- a/intercom/api_operations/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/intercom/api_operations/all.py b/intercom/api_operations/all.py deleted file mode 100644 index 64d9fd94..00000000 --- a/intercom/api_operations/all.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom import utils -from intercom.collection_proxy import CollectionProxy - - -class All(object): - - @classmethod - def all(cls): - collection = utils.resource_class_to_collection_name(cls) - finder_url = "/%s" % (collection) - return CollectionProxy(cls, collection, finder_url) diff --git a/intercom/api_operations/count.py b/intercom/api_operations/count.py deleted file mode 100644 index 70a22e18..00000000 --- a/intercom/api_operations/count.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom import utils - - -class Count(object): - - @classmethod - def count(cls): - from intercom import Intercom - response = Intercom.get("/counts/") - return response[utils.resource_class_to_name(cls)]['count'] diff --git a/intercom/api_operations/delete.py b/intercom/api_operations/delete.py deleted file mode 100644 index 5bc33f44..00000000 --- a/intercom/api_operations/delete.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom import utils - - -class Delete(object): - - def delete(self): - from intercom import Intercom - collection = utils.resource_class_to_collection_name(self.__class__) - Intercom.delete("/%s/%s/" % (collection, self.id)) - return self diff --git a/intercom/api_operations/find.py b/intercom/api_operations/find.py deleted file mode 100644 index f1ba7886..00000000 --- a/intercom/api_operations/find.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom import HttpError -from intercom import utils - - -class Find(object): - - @classmethod - def find(cls, **params): - from intercom import Intercom - collection = utils.resource_class_to_collection_name(cls) - if 'id' in params: - response = Intercom.get("/%s/%s" % (collection, params['id'])) - else: - response = Intercom.get("/%s" % (collection), **params) - - if response is None: - raise HttpError('Http Error - No response entity returned') - - return cls(**response) diff --git a/intercom/api_operations/find_all.py b/intercom/api_operations/find_all.py deleted file mode 100644 index 0f8687c4..00000000 --- a/intercom/api_operations/find_all.py +++ /dev/null @@ -1,17 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom import utils -from intercom.collection_proxy import CollectionProxy - - -class FindAll(object): - - @classmethod - def find_all(cls, **params): - collection = utils.resource_class_to_collection_name(cls) - if 'id' in params and 'type' not in params: - finder_url = "/%s/%s" % (collection, params['id']) - else: - finder_url = "/%s" % (collection) - finder_params = params - return CollectionProxy(cls, collection, finder_url, finder_params) diff --git a/intercom/api_operations/load.py b/intercom/api_operations/load.py deleted file mode 100644 index 9662e8ce..00000000 --- a/intercom/api_operations/load.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom import HttpError -from intercom import utils - - -class Load(object): - - def load(self): - from intercom import Intercom - cls = self.__class__ - collection = utils.resource_class_to_collection_name(cls) - if hasattr(self, 'id'): - response = Intercom.get("/%s/%s" % (collection, self.id)) - else: - raise Exception( - "Cannot load %s as it does not have a valid id." % (cls)) - - if response is None: - raise HttpError('Http Error - No response entity returned') - - return cls(**response) diff --git a/intercom/api_operations/save.py b/intercom/api_operations/save.py deleted file mode 100644 index 61872e9c..00000000 --- a/intercom/api_operations/save.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom import utils - - -class Save(object): - - @classmethod - def create(cls, **params): - from intercom import Intercom - collection = utils.resource_class_to_collection_name(cls) - response = Intercom.post("/%s/" % (collection), **params) - if response: # may be empty if we received a 202 - return cls(**response) - - def from_dict(self, pdict): - for key, value in list(pdict.items()): - setattr(self, key, value) - - @property - def to_dict(self): - a_dict = {} - for name in list(self.__dict__.keys()): - if name == "changed_attributes": - continue - a_dict[name] = self.__dict__[name] # direct access - return a_dict - - @classmethod - def from_api(cls, response): - obj = cls() - obj.from_response(response) - return obj - - def from_response(self, response): - self.from_dict(response) - return self - - def save(self): - from intercom import Intercom - collection = utils.resource_class_to_collection_name(self.__class__) - params = self.attributes - if self.id_present and not self.posted_updates: - # update - response = Intercom.put('/%s/%s' % (collection, self.id), **params) - else: - # create - params.update(self.identity_hash) - response = Intercom.post('/%s' % (collection), **params) - if response: - return self.from_response(response) - - @property - def id_present(self): - return getattr(self, 'id', None) and self.id != "" - - @property - def posted_updates(self): - return getattr(self, 'update_verb', None) == 'post' - - @property - def identity_hash(self): - identity_vars = getattr(self, 'identity_vars', []) - parts = {} - for var in identity_vars: - id_var = getattr(self, var, None) - if id_var: # only present id var if it is not blank or None - parts[var] = id_var - return parts diff --git a/intercom/collection_proxy.py b/intercom/collection_proxy.py deleted file mode 100644 index b86e0d20..00000000 --- a/intercom/collection_proxy.py +++ /dev/null @@ -1,89 +0,0 @@ -# -*- coding: utf-8 -*- - -import six -from intercom import HttpError - - -class CollectionProxy(six.Iterator): - - def __init__(self, cls, collection, finder_url, finder_params={}): - # needed to create class instances of the resource - self.collection_cls = cls - - # needed to reference the collection in the response - self.collection = collection - - # the original URL to retrieve the resources - self.finder_url = finder_url - - # the params to filter the resources - self.finder_params = finder_params - - # an iterator over the resources found in the response - self.resources = None - - # a link to the next page of results - self.next_page = None - - def __iter__(self): - return self - - def __next__(self): - if self.resources is None: - # get the first page of results - self.get_first_page() - - # try to get a resource if there are no more in the - # current resource iterator (StopIteration is raised) - # try to get the next page of results first - try: - resource = six.next(self.resources) - except StopIteration: - self.get_next_page() - resource = six.next(self.resources) - - instance = self.collection_cls(**resource) - return instance - - def __getitem__(self, index): - for i in range(index): - six.next(self) - return six.next(self) - - def get_first_page(self): - # get the first page of results - return self.get_page(self.finder_url, self.finder_params) - - def get_next_page(self): - # get the next page of results - return self.get_page(self.next_page) - - def get_page(self, url, params={}): - # get a page of results - from intercom import Intercom - - # if there is no url stop iterating - if url is None: - raise StopIteration - - response = Intercom.get(url, **params) - if response is None: - raise HttpError('Http Error - No response entity returned') - - collection = response[self.collection] - # if there are no resources in the response stop iterating - if collection is None: - raise StopIteration - - # create the resource iterator - self.resources = iter(collection) - # grab the next page URL if one exists - self.next_page = self.extract_next_link(response) - - def paging_info_present(self, response): - return 'pages' in response and 'type' in response['pages'] - - def extract_next_link(self, response): - if self.paging_info_present(response): - paging_info = response["pages"] - return paging_info["next"] diff --git a/intercom/company.py b/intercom/company.py deleted file mode 100644 index efd40889..00000000 --- a/intercom/company.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom.api_operations.all import All -from intercom.api_operations.count import Count -from intercom.api_operations.delete import Delete -from intercom.api_operations.find import Find -from intercom.api_operations.load import Load -from intercom.api_operations.save import Save -from intercom.extended_api_operations.users import Users -from intercom.traits.api_resource import Resource - - -class Company(Resource, Delete, Count, Find, All, Save, Load, Users): - update_verb = 'post' - identity_vars = ['id', 'company_id'] - - @property - def flat_store_attributes(self): - return ['custom_attributes'] diff --git a/intercom/conversation.py b/intercom/conversation.py deleted file mode 100644 index 7a8780a5..00000000 --- a/intercom/conversation.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom.api_operations.find_all import FindAll -from intercom.api_operations.find import Find -from intercom.api_operations.load import Load -from intercom.api_operations.save import Save -from intercom.extended_api_operations.reply import Reply -from intercom.traits.api_resource import Resource - - -class Conversation(Resource, FindAll, Find, Load, Save, Reply): - pass diff --git a/intercom/count.py b/intercom/count.py deleted file mode 100644 index acb335c2..00000000 --- a/intercom/count.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- - -import six - -from intercom.api_operations.find import Find -from intercom.generic_handlers.count import Counter -from intercom.generic_handlers.base_handler import BaseHandler -from intercom.api_operations.count import Count as CountOperation -from intercom.traits.api_resource import Resource - - -@six.add_metaclass(BaseHandler) -class Count(Resource, Find, CountOperation, Counter): - - @classmethod - def fetch_for_app(cls): - return Count.find() - - @classmethod - def do_broken_down_count(cls, entity_to_count, count_context): - result = cls.fetch_broken_down_count(entity_to_count, count_context) - return getattr(result, entity_to_count)[count_context] - - @classmethod - def fetch_broken_down_count(cls, entity_to_count, count_context): - return Count.find(type=entity_to_count, count=count_context) diff --git a/intercom/errors.py b/intercom/errors.py deleted file mode 100644 index 589aa709..00000000 --- a/intercom/errors.py +++ /dev/null @@ -1,68 +0,0 @@ -# -*- coding: utf-8 -*- - - -class IntercomError(Exception): - - def __init__(self, message=None, context=None): - super(IntercomError, self).__init__(message) - self.message = message - self.context = context - - -class ArgumentError(ValueError, IntercomError): - pass - - -class HttpError(IntercomError): - pass - - -class ResourceNotFound(IntercomError): - pass - - -class AuthenticationError(IntercomError): - pass - - -class ServerError(IntercomError): - pass - - -class BadGatewayError(IntercomError): - pass - - -class ServiceUnavailableError(IntercomError): - pass - - -class BadRequestError(IntercomError): - pass - - -class RateLimitExceeded(IntercomError): - pass - - -class MultipleMatchingUsersError(IntercomError): - pass - - -class UnexpectedError(IntercomError): - pass - - -error_codes = { - 'unauthorized': AuthenticationError, - 'forbidden': AuthenticationError, - 'bad_request': BadRequestError, - 'missing_parameter': BadRequestError, - 'parameter_invalid': BadRequestError, - 'parameter_not_found': BadRequestError, - 'not_found': ResourceNotFound, - 'rate_limit_exceeded': RateLimitExceeded, - 'service_unavailable': ServiceUnavailableError, - 'conflict': MultipleMatchingUsersError, - 'unique_user_constraint': MultipleMatchingUsersError -} diff --git a/intercom/event.py b/intercom/event.py deleted file mode 100644 index ee05f2b2..00000000 --- a/intercom/event.py +++ /dev/null @@ -1,9 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom.api_operations.find import Find -from intercom.api_operations.save import Save -from intercom.traits.api_resource import Resource - - -class Event(Resource, Save, Find): - pass diff --git a/intercom/events.py b/intercom/events.py deleted file mode 100644 index 971816fa..00000000 --- a/intercom/events.py +++ /dev/null @@ -1,40 +0,0 @@ -# coding=utf-8 -# -# Copyright 2014 martin@mekkaoui.fr -# -# License: MIT -# -""" Intercom API wrapper. """ - -from . import Intercom -from .user import UserId - - -class Event(UserId): - - @classmethod - def create(cls, event_name=None, user_id=None, email=None, metadata=None): - resp = Intercom.create_event(event_name=event_name, user_id=user_id, email=email, metadata=metadata) - return Event(resp) - - def save(self): - """ Create an Event from this objects properties: - - >>> event = Event() - >>> event.event_name = "shared-item" - >>> event.email = "joe@example.com" - >>> event.save() - - """ - resp = Intercom.create_event(**self) - self.update(resp) - - @property - def event_name(self): - """ The name of the Event. """ - return dict.get(self, 'event_name', None) - - @event_name.setter - def event_name(self, event_name): - """ Set the event name. """ - self['event_name'] = event_name diff --git a/intercom/extended_api_operations/__init__.py b/intercom/extended_api_operations/__init__.py deleted file mode 100644 index 40a96afc..00000000 --- a/intercom/extended_api_operations/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/intercom/extended_api_operations/reply.py b/intercom/extended_api_operations/reply.py deleted file mode 100644 index 30ff089c..00000000 --- a/intercom/extended_api_operations/reply.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom import utils - - -class Reply(object): - - def reply(self, **reply_data): - return self.__reply(reply_data) - - def close_conversation(self, **reply_data): - reply_data['type'] = 'admin' - reply_data['message_type'] = 'close' - return self.__reply(reply_data) - - def open_conversation(self, **reply_data): - reply_data['type'] = 'admin' - reply_data['message_type'] = 'open' - return self.__reply(reply_data) - - def assign(self, **reply_data): - reply_data['type'] = 'admin' - reply_data['message_type'] = 'assignment' - return self.__reply(reply_data) - - def __reply(self, reply_data): - from intercom import Intercom - collection = utils.resource_class_to_collection_name(self.__class__) - url = "/%s/%s/reply" % (collection, self.id) - reply_data['conversation_id'] = self.id - response = Intercom.post(url, **reply_data) - return self.from_response(response) diff --git a/intercom/extended_api_operations/users.py b/intercom/extended_api_operations/users.py deleted file mode 100644 index 39e0dcef..00000000 --- a/intercom/extended_api_operations/users.py +++ /dev/null @@ -1,14 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom import utils -from intercom.user import User -from intercom.collection_proxy import CollectionProxy - - -class Users(object): - - @property - def users(self): - collection = utils.resource_class_to_collection_name(self.__class__) - finder_url = "/%s/%s/users" % (collection, self.id) - return CollectionProxy(User, "users", finder_url) diff --git a/intercom/generic_handlers/__init__.py b/intercom/generic_handlers/__init__.py deleted file mode 100644 index 40a96afc..00000000 --- a/intercom/generic_handlers/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/intercom/generic_handlers/base_handler.py b/intercom/generic_handlers/base_handler.py deleted file mode 100644 index 48ea961b..00000000 --- a/intercom/generic_handlers/base_handler.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- - -import inspect - - -class BaseHandler(type): - - def __getattr__(cls, name): # noqa - # ignore underscore attrs - if name[0] == "_": - return - - # get the class heirarchy - klasses = inspect.getmro(cls) - # find a class that can handle this attr - for klass in klasses: - if hasattr(klass, 'handles_attr') and klass.handles_attr(name): - return klass._get(cls, name) diff --git a/intercom/generic_handlers/count.py b/intercom/generic_handlers/count.py deleted file mode 100644 index 97bbf500..00000000 --- a/intercom/generic_handlers/count.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- - -import re - - -class Counter(): - - count_breakdown_matcher = re.compile(r'(\w+)_counts_for_each_(\w+)') - - @classmethod - def handles_attr(cls, name): - return cls.count_breakdown_matcher.search(name) is not None - - @classmethod - def _get(cls, entity, name): - match = cls.count_breakdown_matcher.search(name) - entity_to_count = match.group(1) - count_context = match.group(2) - return entity.do_broken_down_count(entity_to_count, count_context) diff --git a/intercom/generic_handlers/tag.py b/intercom/generic_handlers/tag.py deleted file mode 100644 index 52fa73c4..00000000 --- a/intercom/generic_handlers/tag.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom import utils - - -class TagHandler(): - def __init__(self, entity, name, context): - self.entity = entity - self.untag = name == "untag" - self.context = context - - def __call__(self, *args, **kwargs): - return self.entity._tag_collection( - self.context, *args, untag=self.untag, **kwargs) - - -class TagUntag(): - - @classmethod - def handles_attr(cls, name): - name, context = name.split('_', 1) - if name in ["tag", "untag"]: - return True - - @classmethod - def _get(cls, entity, name): - name, context = name.split('_', 1) - return TagHandler(entity, name, context) - - @classmethod - def _tag_collection( - cls, collection_name, name, objects, untag=False): - from intercom import Intercom - collection = utils.resource_class_to_collection_name(cls) - object_ids = [] - for obj in objects: - if not hasattr(obj, 'keys'): - obj = {'id': obj} - if untag: - obj['untag'] = True - object_ids.append(obj) - - params = { - 'name': name, - collection_name: object_ids, - } - response = Intercom.post("/%s" % (collection), **params) - return cls(**response) diff --git a/intercom/generic_handlers/tag_find_all.py b/intercom/generic_handlers/tag_find_all.py deleted file mode 100644 index a971802f..00000000 --- a/intercom/generic_handlers/tag_find_all.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- - -import re - - -class FindAllHandler(): - def __init__(self, entity, context): - self.entity = entity - self.context = context - - def __call__(self, *args, **kwargs): - return self.entity._find_all_for( - self.context, *args, **kwargs) - - -class TagFindAll(): - - find_matcher = re.compile(r'find_all_for_(\w+)') - - @classmethod - def handles_attr(cls, name): - return cls.find_matcher.search(name) is not None - - @classmethod - def _get(cls, entity, name): - match = cls.find_matcher.search(name) - context = match.group(1) - return FindAllHandler(entity, context) - - @classmethod - def _find_all_for(cls, taggable_type, **kwargs): - params = { - 'taggable_type': taggable_type - } - res_id = kwargs.pop('id', None) - if res_id: - params['taggable_id'] = res_id - params.update(kwargs) - - return cls.find_all(**params) diff --git a/intercom/lib/__init__.py b/intercom/lib/__init__.py deleted file mode 100644 index 40a96afc..00000000 --- a/intercom/lib/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/intercom/lib/flat_store.py b/intercom/lib/flat_store.py deleted file mode 100644 index f4c10c36..00000000 --- a/intercom/lib/flat_store.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- - -import numbers -import six - - -class FlatStore(dict): - - def __init__(self, *args, **kwargs): - self.update(*args, **kwargs) - - def __setitem__(self, key, value): - if not ( - isinstance(value, numbers.Real) or - isinstance(value, six.string_types) or - value is None - ): - raise ValueError( - "custom data only allows None, string and real number values") - if not isinstance(key, six.string_types): - raise ValueError("custom data only allows string keys") - super(FlatStore, self).__setitem__(key, value) - - def update(self, *args, **kwargs): - if args: - if len(args) > 1: - raise TypeError("update expected at most 1 arguments, " - "got %d" % len(args)) - other = dict(args[0]) - for key in other: - self[key] = other[key] - for key in kwargs: - self[key] = kwargs[key] - - def setdefault(self, key, value=None): - if key not in self: - self[key] = value - return self[key] diff --git a/intercom/lib/setter_property.py b/intercom/lib/setter_property.py deleted file mode 100644 index 14eb7bd8..00000000 --- a/intercom/lib/setter_property.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- - -class SetterProperty(object): - - def __init__(self, func, doc=None): - self.func = func - self.__doc__ = doc if doc is not None else func.__doc__ - - def __set__(self, obj, value): - return self.func(obj, value) diff --git a/intercom/lib/typed_json_deserializer.py b/intercom/lib/typed_json_deserializer.py deleted file mode 100644 index 327a558f..00000000 --- a/intercom/lib/typed_json_deserializer.py +++ /dev/null @@ -1,45 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom import utils - - -class JsonDeserializer(object): - - def __init__(self, json): - self._json = json - self._object_type = None - - @property - def _get_object_type(self): - if self._object_type is None: - self._object_type = self._json.get('type', None) - if self._object_type is None: - raise Exception( - 'No type field found to faciliate deserialization') - return self._object_type - - @property - def _is_list_type(self): - return self._get_object_type.endswith('.list') - - @property - def _object_entity_key(self): - return utils.entity_key_from_type(self._get_object_type) - - def deserialize(self): - if self._is_list_type: - return self.deserialize_collection( - self._json[self._object_entity_key]) - else: - return self.deserialize_object(self._json) - - def deserialize_collection(self, collection_json): - if collection_json is None: - return [] - return [JsonDeserializer(object_json).deserialize() - for object_json in collection_json] - - def deserialize_object(self, object_json): - entity_class = utils.constantize_singular_resource_name( - self._object_entity_key) - return entity_class.from_api(object_json) diff --git a/intercom/message.py b/intercom/message.py deleted file mode 100644 index 4a0d38d0..00000000 --- a/intercom/message.py +++ /dev/null @@ -1,8 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom.api_operations.save import Save -from intercom.traits.api_resource import Resource - - -class Message(Resource, Save): - pass diff --git a/intercom/note.py b/intercom/note.py deleted file mode 100644 index 59a56499..00000000 --- a/intercom/note.py +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom.api_operations.find_all import FindAll -from intercom.api_operations.find import Find -from intercom.api_operations.save import Save -from intercom.api_operations.load import Load -from intercom.traits.api_resource import Resource - - -class Note(Resource, Find, FindAll, Load, Save): - pass diff --git a/intercom/notification.py b/intercom/notification.py deleted file mode 100644 index 2209ff51..00000000 --- a/intercom/notification.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom.traits.api_resource import Resource - - -class Notification(Resource): - - @property - def model(self): - return self.data.item - - @property - def model_type(self): - return self.model.__class__ - - @property - def load(self): - return self.model.load diff --git a/intercom/request.py b/intercom/request.py deleted file mode 100644 index c4002d5c..00000000 --- a/intercom/request.py +++ /dev/null @@ -1,150 +0,0 @@ -# -*- coding: utf-8 -*- - -from . import errors -from datetime import datetime - -import certifi -import json -import logging -import requests - -logger = logging.getLogger('intercom.request') - - -class Request(object): - - timeout = 10 - - @classmethod - def send_request_to_path(cls, method, url, auth, params=None): - """ Construct an API request, send it to the API, and parse the - response. """ - from intercom import __version__ - req_params = {} - - headers = { - 'User-Agent': 'python-intercom/' + __version__, - 'Accept': 'application/json' - } - if method in ('POST', 'PUT', 'DELETE'): - headers['content-type'] = 'application/json' - req_params['data'] = json.dumps(params, cls=ResourceEncoder) - elif method == 'GET': - req_params['params'] = params - req_params['headers'] = headers - - # request logging - if logger.isEnabledFor(logging.DEBUG): - logger.debug("Sending %s request to: %s", method, url) - logger.debug(" headers: %s", headers) - if method == 'GET': - logger.debug(" params: %s", req_params['params']) - else: - logger.debug(" params: %s", req_params['data']) - - resp = requests.request( - method, url, timeout=cls.timeout, - auth=auth, verify=certifi.where(), **req_params) - - # response logging - if logger.isEnabledFor(logging.DEBUG): - logger.debug("Response received from %s", url) - logger.debug(" encoding=%s status:%s", - resp.encoding, resp.status_code) - logger.debug(" content:\n%s", resp.content) - - cls.raise_errors_on_failure(resp) - cls.set_rate_limit_details(resp) - - if resp.content and resp.content.strip(): - # parse non empty bodies - return cls.parse_body(resp) - - @classmethod - def parse_body(cls, resp): - try: - # use supplied or inferred encoding to decode the - # response content - decoded_body = resp.content.decode( - resp.encoding or resp.apparent_encoding) - body = json.loads(decoded_body) - if body.get('type') == 'error.list': - cls.raise_application_errors_on_failure(body, resp.status_code) - return body - except ValueError: - cls.raise_errors_on_failure(resp) - - @classmethod - def set_rate_limit_details(cls, resp): - rate_limit_details = {} - headers = resp.headers - limit = headers.get('x-ratelimit-limit', None) - remaining = headers.get('x-ratelimit-remaining', None) - reset = headers.get('x-ratelimit-reset', None) - if limit: - rate_limit_details['limit'] = int(limit) - if remaining: - rate_limit_details['remaining'] = int(remaining) - if reset: - rate_limit_details['reset_at'] = datetime.fromtimestamp(int(reset)) - from intercom import Intercom - Intercom.rate_limit_details = rate_limit_details - - @classmethod - def raise_errors_on_failure(cls, resp): - if resp.status_code == 404: - raise errors.ResourceNotFound('Resource Not Found') - elif resp.status_code == 401: - raise errors.AuthenticationError('Unauthorized') - elif resp.status_code == 403: - raise errors.AuthenticationError('Forbidden') - elif resp.status_code == 500: - raise errors.ServerError('Server Error') - elif resp.status_code == 502: - raise errors.BadGatewayError('Bad Gateway Error') - elif resp.status_code == 503: - raise errors.ServiceUnavailableError('Service Unavailable') - - @classmethod - def raise_application_errors_on_failure(cls, error_list_details, http_code): # noqa - # Currently, we don't support multiple errors - error_details = error_list_details['errors'][0] - error_code = error_details.get('type') - if error_code is None: - error_code = error_details.get('code') - error_context = { - 'http_code': http_code, - 'application_error_code': error_code - } - error_class = errors.error_codes.get(error_code) - if error_class is None: - # unexpected error - if error_code: - message = cls.message_for_unexpected_error_with_type( - error_details, http_code) - else: - message = cls.message_for_unexpected_error_without_type( - error_details, http_code) - error_class = errors.UnexpectedError - else: - message = error_details.get('message') - raise error_class(message, error_context) - - @classmethod - def message_for_unexpected_error_with_type(cls, error_details, http_code): # noqa - error_type = error_details.get('type') - message = error_details.get('message') - return "The error of type '%s' is not recognized. It occurred with the message: %s and http_code: '%s'. Please contact Intercom with these details." % (error_type, message, http_code) # noqa - - @classmethod - def message_for_unexpected_error_without_type(cls, error_details, http_code): # noqa - message = error_details['message'] - return "An unexpected error occured. It occurred with the message: %s and http_code: '%s'. Please contact Intercom with these details." % (message, http_code) # noqa - - -class ResourceEncoder(json.JSONEncoder): - def default(self, o): - if hasattr(o, 'attributes'): - # handle API resources - return o.attributes - return super(ResourceEncoder, self).default(o) diff --git a/intercom/segment.py b/intercom/segment.py deleted file mode 100644 index 1c3d3d39..00000000 --- a/intercom/segment.py +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom.api_operations.all import All -from intercom.api_operations.count import Count -from intercom.api_operations.find import Find -from intercom.api_operations.save import Save -from intercom.traits.api_resource import Resource - - -class Segment(Resource, Find, Count, Save, All): - pass diff --git a/intercom/subscription.py b/intercom/subscription.py deleted file mode 100644 index f93056e1..00000000 --- a/intercom/subscription.py +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom.api_operations.find import Find -from intercom.api_operations.delete import Delete -from intercom.api_operations.find_all import FindAll -from intercom.api_operations.save import Save -from intercom.traits.api_resource import Resource - - -class Subscription(Resource, Find, FindAll, Save, Delete): - pass diff --git a/intercom/tag.py b/intercom/tag.py deleted file mode 100644 index fd59dcd9..00000000 --- a/intercom/tag.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- - -import six - -from intercom.api_operations.all import All -from intercom.api_operations.count import Count -from intercom.api_operations.find import Find -from intercom.api_operations.find_all import FindAll -from intercom.api_operations.save import Save -from intercom.generic_handlers.base_handler import BaseHandler -from intercom.generic_handlers.tag import TagUntag -from intercom.generic_handlers.tag_find_all import TagFindAll -from intercom.traits.api_resource import Resource - - -@six.add_metaclass(BaseHandler) -class Tag(Resource, All, Count, Find, FindAll, Save, TagUntag, TagFindAll): - pass diff --git a/intercom/traits/__init__.py b/intercom/traits/__init__.py deleted file mode 100644 index 40a96afc..00000000 --- a/intercom/traits/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/intercom/traits/api_resource.py b/intercom/traits/api_resource.py deleted file mode 100644 index af4090f3..00000000 --- a/intercom/traits/api_resource.py +++ /dev/null @@ -1,101 +0,0 @@ -# -*- coding: utf-8 -*- - -import datetime -import time - -from intercom.lib.flat_store import FlatStore -from intercom.lib.typed_json_deserializer import JsonDeserializer - - -def type_field(attribute): - return attribute == "type" - - -def timestamp_field(attribute): - return attribute.endswith('_at') - - -def custom_attribute_field(attribute): - return attribute == 'custom_attributes' - - -def typed_value(value): - return hasattr(value, 'keys') and 'type' in value - - -def datetime_value(value): - return hasattr(value, "timetuple") - - -def to_datetime_value(value): - if value: - return datetime.datetime.fromtimestamp(int(value)) - - -class Resource(object): - changed_attributes = [] - - def __init__(_self, **params): # noqa - # intercom includes a 'self' field in the JSON, to avoid the naming - # conflict we go with _self here - _self.from_dict(params) - - if hasattr(_self, 'flat_store_attributes'): - for attr in _self.flat_store_attributes: - if not hasattr(_self, attr): - setattr(_self, attr, FlatStore()) - - def _flat_store_attribute(self, attribute): - if hasattr(self, 'flat_store_attributes'): - return attribute in self.flat_store_attributes - return False - - @classmethod - def from_api(cls, response): - obj = cls() - obj.from_response(response) - return obj - - def from_response(self, response): - self.from_dict(response) - return self - - def from_dict(self, dict): - for attribute, value in list(dict.items()): - if type_field(attribute): - continue - setattr(self, attribute, value) - if hasattr(self, 'id'): - # already exists in Intercom - self.changed_attributes = [] - - @property - def attributes(self): - res = {} - for name, value in list(self.__dict__.items()): - if self.submittable_attribute(name, value): - res[name] = value - return res - - def submittable_attribute(self, name, value): - return name in self.changed_attributes or (isinstance(value, FlatStore) and name in self.flat_store_attributes) # noqa - - def __getattribute__(self, attribute): - value = super(Resource, self).__getattribute__(attribute) - if timestamp_field(attribute): - return to_datetime_value(value) - else: - return value - - def __setattr__(self, attribute, value): - if typed_value(value) and not custom_attribute_field(attribute): - value_to_set = JsonDeserializer(value).deserialize() - elif self._flat_store_attribute(attribute): - value_to_set = FlatStore(value) - elif timestamp_field(attribute) and datetime_value(value): - value_to_set = time.mktime(value.timetuple()) - else: - value_to_set = value - if attribute != 'changed_attributes': - self.changed_attributes.append(attribute) - super(Resource, self).__setattr__(attribute, value_to_set) diff --git a/intercom/traits/incrementable_attributes.py b/intercom/traits/incrementable_attributes.py deleted file mode 100644 index de608ec2..00000000 --- a/intercom/traits/incrementable_attributes.py +++ /dev/null @@ -1,8 +0,0 @@ -# -*- coding: utf-8 -*- - - -class IncrementableAttributes(object): - - def increment(self, key, value=1): - existing_value = self.custom_attributes.get(key, 0) - self.custom_attributes[key] = existing_value + value diff --git a/intercom/user.py b/intercom/user.py deleted file mode 100644 index f57f533a..00000000 --- a/intercom/user.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- - -from intercom.api_operations.all import All -from intercom.api_operations.count import Count -from intercom.api_operations.delete import Delete -from intercom.api_operations.find import Find -from intercom.api_operations.find_all import FindAll -from intercom.api_operations.load import Load -from intercom.api_operations.save import Save -from intercom.traits.api_resource import Resource -from intercom.traits.incrementable_attributes import IncrementableAttributes - - -class User(Resource, Find, FindAll, All, Count, Load, Save, Delete, - IncrementableAttributes): - - update_verb = 'post' - identity_vars = ['email', 'user_id'] - - @property - def flat_store_attributes(self): - return ['custom_attributes'] diff --git a/intercom/utils.py b/intercom/utils.py deleted file mode 100644 index 4319339b..00000000 --- a/intercom/utils.py +++ /dev/null @@ -1,56 +0,0 @@ -# -*- coding: utf-8 -*- - -import inflection -import six - - -def pluralize(str): - return inflection.pluralize(str) - - -def entity_key_from_type(type): - if '.' in type: - is_list = type.split('.')[1] == 'list' - entity_name = type.split('.')[0] - if is_list: - return pluralize(entity_name) - else: - entity_name = type - return entity_name - - -def constantize_singular_resource_name(resource_name): - class_name = inflection.camelize(resource_name) - return create_class_instance(class_name) - - -def resource_class_to_collection_name(cls): - return pluralize(cls.__name__.lower()) - - -def resource_class_to_name(cls): - return cls.__name__.lower() - - -CLASS_REGISTRY = {} - - -def create_class_instance(class_name): - from intercom.api_operations.load import Load - from intercom.traits.api_resource import Resource - - if class_name in CLASS_REGISTRY: - return CLASS_REGISTRY[class_name] - - class Meta(type): - def __new__(cls, name, bases, attributes): - return super(Meta, cls).__new__( - cls, str(class_name), bases, attributes) - - @six.add_metaclass(Meta) - class DynamicClass(Resource, Load): - pass - - dyncls = DynamicClass() - CLASS_REGISTRY[class_name] = dyncls - return dyncls diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 00000000..9decbf87 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,594 @@ +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} + +[[package]] +name = "anyio" +version = "4.5.2" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "anyio-4.5.2-py3-none-any.whl", hash = "sha256:c011ee36bc1e8ba40e5a81cb9df91925c218fe9b778554e0b56a21e1b5d4716f"}, + {file = "anyio-4.5.2.tar.gz", hash = "sha256:23009af4ed04ce05991845451e11ef02fc7c5ed29179ac9a420e5ad0ac7ddc5b"}, +] + +[package.dependencies] +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} +idna = ">=2.8" +sniffio = ">=1.1" +typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} + +[package.extras] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +trio = ["trio (>=0.26.1)"] + +[[package]] +name = "certifi" +version = "2025.11.12" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.7" +files = [ + {file = "certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b"}, + {file = "certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, + {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "execnet" +version = "2.1.2" +description = "execnet: rapid multi-Python deployment" +optional = false +python-versions = ">=3.8" +files = [ + {file = "execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec"}, + {file = "execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd"}, +] + +[package.extras] +testing = ["hatch", "pre-commit", "pytest", "tox"] + +[[package]] +name = "h11" +version = "0.16.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.8" +files = [ + {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, + {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, + {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.16" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<1.0)"] + +[[package]] +name = "httpx" +version = "0.28.1" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, + {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "idna" +version = "3.11" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, + {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.8" +files = [ + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, +] + +[[package]] +name = "mypy" +version = "1.13.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"}, + {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"}, + {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"}, + {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"}, + {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"}, + {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"}, + {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"}, + {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"}, + {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"}, + {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"}, + {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"}, + {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"}, + {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"}, + {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"}, + {file = "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"}, + {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"}, + {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"}, + {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"}, + {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"}, + {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"}, + {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"}, + {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.6.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, +] + +[[package]] +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pydantic" +version = "2.10.6" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, + {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.27.2" +typing-extensions = ">=4.12.2" + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] + +[[package]] +name = "pydantic-core" +version = "2.27.2" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, + {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, + {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, + {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, + {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, + {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, + {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, + {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, + {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, + {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, + {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, + {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, + {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, + {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, + {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, + {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, + {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, + {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, + {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, + {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, + {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, + {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, + {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, + {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, + {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, + {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pytest" +version = "7.4.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.23.8" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_asyncio-0.23.8-py3-none-any.whl", hash = "sha256:50265d892689a5faefb84df80819d1ecef566eb3549cf915dfb33569359d1ce2"}, + {file = "pytest_asyncio-0.23.8.tar.gz", hash = "sha256:759b10b33a6dc61cce40a8bd5205e302978bbbcc00e279a8b61d9a6a3c82e4d3"}, +] + +[package.dependencies] +pytest = ">=7.0.0,<9" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + +[[package]] +name = "pytest-xdist" +version = "3.6.1" +description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, + {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, +] + +[package.dependencies] +execnet = ">=2.1" +pytest = ">=7.0.0" + +[package.extras] +psutil = ["psutil (>=3.0)"] +setproctitle = ["setproctitle"] +testing = ["filelock"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "ruff" +version = "0.11.5" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.11.5-py3-none-linux_armv6l.whl", hash = "sha256:2561294e108eb648e50f210671cc56aee590fb6167b594144401532138c66c7b"}, + {file = "ruff-0.11.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac12884b9e005c12d0bd121f56ccf8033e1614f736f766c118ad60780882a077"}, + {file = "ruff-0.11.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4bfd80a6ec559a5eeb96c33f832418bf0fb96752de0539905cf7b0cc1d31d779"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0947c0a1afa75dcb5db4b34b070ec2bccee869d40e6cc8ab25aca11a7d527794"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ad871ff74b5ec9caa66cb725b85d4ef89b53f8170f47c3406e32ef040400b038"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6cf918390cfe46d240732d4d72fa6e18e528ca1f60e318a10835cf2fa3dc19f"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:56145ee1478582f61c08f21076dc59153310d606ad663acc00ea3ab5b2125f82"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e5f66f8f1e8c9fc594cbd66fbc5f246a8d91f916cb9667e80208663ec3728304"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80b4df4d335a80315ab9afc81ed1cff62be112bd165e162b5eed8ac55bfc8470"}, + {file = "ruff-0.11.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3068befab73620b8a0cc2431bd46b3cd619bc17d6f7695a3e1bb166b652c382a"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f5da2e710a9641828e09aa98b92c9ebbc60518fdf3921241326ca3e8f8e55b8b"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ef39f19cb8ec98cbc762344921e216f3857a06c47412030374fffd413fb8fd3a"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b2a7cedf47244f431fd11aa5a7e2806dda2e0c365873bda7834e8f7d785ae159"}, + {file = "ruff-0.11.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:81be52e7519f3d1a0beadcf8e974715b2dfc808ae8ec729ecfc79bddf8dbb783"}, + {file = "ruff-0.11.5-py3-none-win32.whl", hash = "sha256:e268da7b40f56e3eca571508a7e567e794f9bfcc0f412c4b607931d3af9c4afe"}, + {file = "ruff-0.11.5-py3-none-win_amd64.whl", hash = "sha256:6c6dc38af3cfe2863213ea25b6dc616d679205732dc0fb673356c2d69608f800"}, + {file = "ruff-0.11.5-py3-none-win_arm64.whl", hash = "sha256:67e241b4314f4eacf14a601d586026a962f4002a475aa702c69980a38087aa4e"}, + {file = "ruff-0.11.5.tar.gz", hash = "sha256:cae2e2439cb88853e421901ec040a758960b576126dab520fa08e9de431d1bef"}, +] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "tomli" +version = "2.3.0" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45"}, + {file = "tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba"}, + {file = "tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf"}, + {file = "tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441"}, + {file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845"}, + {file = "tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c"}, + {file = "tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456"}, + {file = "tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be"}, + {file = "tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac"}, + {file = "tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22"}, + {file = "tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f"}, + {file = "tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52"}, + {file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8"}, + {file = "tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6"}, + {file = "tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876"}, + {file = "tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878"}, + {file = "tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b"}, + {file = "tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae"}, + {file = "tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b"}, + {file = "tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf"}, + {file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f"}, + {file = "tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05"}, + {file = "tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606"}, + {file = "tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999"}, + {file = "tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e"}, + {file = "tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3"}, + {file = "tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc"}, + {file = "tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0"}, + {file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879"}, + {file = "tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005"}, + {file = "tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463"}, + {file = "tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8"}, + {file = "tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77"}, + {file = "tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf"}, + {file = "tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530"}, + {file = "tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b"}, + {file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67"}, + {file = "tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f"}, + {file = "tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0"}, + {file = "tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba"}, + {file = "tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b"}, + {file = "tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549"}, +] + +[[package]] +name = "types-python-dateutil" +version = "2.9.0.20241206" +description = "Typing stubs for python-dateutil" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, + {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, +] + +[[package]] +name = "typing-extensions" +version = "4.13.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, + {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.8" +content-hash = "bcf31a142c86d9e556553c8c260a93b563ac64a043076dbd48b26111d422c26e" diff --git a/pylint.conf b/pylint.conf deleted file mode 100644 index 66b31ef9..00000000 --- a/pylint.conf +++ /dev/null @@ -1,249 +0,0 @@ -[MASTER] - -# Specify a configuration file. -#rcfile= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Profiled execution. -profile=no - -# Add files or directories to the blacklist. They should be base names, not -# paths. -ignore=CVS - -# Pickle collected data for later comparisons. -persistent=yes - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= - - -[MESSAGES CONTROL] - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time. -#enable= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). -disable=W0142 - - -[REPORTS] - -# Set the output format. Available formats are text, parseable, colorized, msvs -# (visual studio) and html -output-format=text - -# Include message's id in output -include-ids=no - -# Put messages in a separate file for each module / package specified on the -# command line instead of printing them on stdout. Reports (if any) will be -# written in a file name "pylint_global.[txt|html]". -files-output=no - -# Tells whether to display a full report or only the messages -reports=yes - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Add a comment according to your evaluation note. This is used by the global -# evaluation report (RP0004). -comment=no - - -[BASIC] - -# Required attributes for module, separated by a comma -required-attributes= - -# List of builtins function names that should not be used, separated by a comma -bad-functions=map,filter,apply,input - -# Regular expression which should only match correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Regular expression which should only match correct module level names -const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Regular expression which should only match correct class names -class-rgx=[A-Z_][a-zA-Z0-9]+$ - -# Regular expression which should only match correct function names -function-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct method names -method-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct instance attribute names -attr-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct argument names -argument-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct variable names -variable-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match correct list comprehension / -# generator expression variable names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - -# Good variable names which should always be accepted, separated by a comma -good-names=i,j,k,ex,Run,_ - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata - -# Regular expression which should only match functions or classes name which do -# not require a docstring -no-docstring-rgx=__.*__ - - -[FORMAT] - -# Maximum number of characters on a single line. -max-line-length=100 - -# Maximum number of lines in a module -max-module-lines=1000 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME,XXX,TODO - - -[SIMILARITIES] - -# Minimum lines number of a similarity. -min-similarity-lines=4 - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - - -[TYPECHECK] - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# List of classes names for which member attributes should not be checked -# (useful for classes with attributes dynamically set). -ignored-classes=SQLObject - -# When zope mode is activated, add a predefined set of Zope acquired attributes -# to generated-members. -zope=no - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E0201 when accessed. Python regular -# expressions are accepted. -generated-members=REQUEST,acl_users,aq_parent - - -[VARIABLES] - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# A regular expression matching the beginning of the name of dummy variables -# (i.e. not used). -dummy-variables-rgx=_|dummy - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - - -[CLASSES] - -# List of interface methods to ignore, separated by a comma. This is used for -# instance to not check methods defines in Zope's Interface base class. -ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - - -[DESIGN] - -# Maximum number of arguments for function / method -max-args=10 - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=_.* - -# Maximum number of locals for function / method body -max-locals=15 - -# Maximum number of return / yield for function / method body -max-returns=6 - -# Maximum number of branch for function / method body -max-branchs=12 - -# Maximum number of statements in function / method body -max-statements=50 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of attributes for a class (see R0902). -max-attributes=7 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=30 - - -[IMPORTS] - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub,string,TERMIOS,Bastion,rexec - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=Exception diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..e348e4f2 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,85 @@ +[project] +name = "python-intercom" + +[tool.poetry] +name = "python-intercom" +version = "5.0.0" +description = "" +readme = "README.md" +authors = [] +keywords = [] + +classifiers = [ + "Intended Audience :: Developers", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Operating System :: OS Independent", + "Operating System :: POSIX", + "Operating System :: MacOS", + "Operating System :: POSIX :: Linux", + "Operating System :: Microsoft :: Windows", + "Topic :: Software Development :: Libraries :: Python Modules", + "Typing :: Typed" +] +packages = [ + { include = "intercom", from = "src"} +] + +[tool.poetry.urls] +Repository = 'https://github.com/intercom/python-intercom' + +[tool.poetry.dependencies] +python = "^3.8" +httpx = ">=0.21.2" +pydantic = ">= 1.9.2" +pydantic-core = ">=2.18.2" +typing_extensions = ">= 4.0.0" + +[tool.poetry.group.dev.dependencies] +mypy = "==1.13.0" +pytest = "^7.4.0" +pytest-asyncio = "^0.23.5" +pytest-xdist = "^3.6.1" +python-dateutil = "^2.9.0" +types-python-dateutil = "^2.9.0.20240316" +ruff = "==0.11.5" + +[tool.pytest.ini_options] +testpaths = [ "tests" ] +asyncio_mode = "auto" + +[tool.mypy] +plugins = ["pydantic.mypy"] + +[tool.ruff] +line-length = 120 + +[tool.ruff.lint] +select = [ + "E", # pycodestyle errors + "F", # pyflakes + "I", # isort +] +ignore = [ + "E402", # Module level import not at top of file + "E501", # Line too long + "E711", # Comparison to `None` should be `cond is not None` + "E712", # Avoid equality comparisons to `True`; use `if ...:` checks + "E721", # Use `is` and `is not` for type comparisons, or `isinstance()` for insinstance checks + "E722", # Do not use bare `except` + "E731", # Do not assign a `lambda` expression, use a `def` + "F821", # Undefined name + "F841" # Local variable ... is assigned to but never used +] + +[tool.ruff.lint.isort] +section-order = ["future", "standard-library", "third-party", "first-party"] + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/reference.md b/reference.md new file mode 100644 index 00000000..f4eb0dca --- /dev/null +++ b/reference.md @@ -0,0 +1,25866 @@ +# Reference +## Admins +
client.admins.identify() +
+
+ +#### 📝 Description + +
+
+ +
+
+ + +You can view the currently authorised admin along with the embedded app object (a "workspace" in legacy terminology). + +> 🚧 Single Sign On +> +> If you are building a custom "Log in with Intercom" flow for your site, and you call the `/me` endpoint to identify the logged-in user, you should not accept any sign-ins from users with unverified email addresses as it poses a potential impersonation security risk. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.admins.identify() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.admins.away(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can set an Admin as away for the Inbox. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.admins.away( + admin_id=1, + away_mode_enabled=True, + away_mode_reassign=True, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**admin_id:** `int` — The unique identifier of a given admin + +
+
+ +
+
+ +**away_mode_enabled:** `bool` — Set to "true" to change the status of the admin to away. + +
+
+ +
+
+ +**away_mode_reassign:** `bool` — Set to "true" to assign any new conversation replies to your default inbox. + +
+
+ +
+
+ +**away_status_reason_id:** `typing.Optional[int]` — The unique identifier of the away status reason + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.admins.list_all_activity_logs(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can get a log of activities by all admins in an app. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.admins.list_all_activity_logs( + created_at_after="1677253093", + created_at_before="1677861493", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**created_at_after:** `str` — The start date that you request data for. It must be formatted as a UNIX timestamp. + +
+
+ +
+
+ +**created_at_before:** `typing.Optional[str]` — The end date that you request data for. It must be formatted as a UNIX timestamp. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.admins.list() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of admins for a given workspace. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.admins.list() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.admins.find(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can retrieve the details of a single admin. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.admins.find( + admin_id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**admin_id:** `int` — The unique identifier of a given admin + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## AI Content +
client.ai_content.list_content_import_sources() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can retrieve a list of all content import sources for a workspace. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.ai_content.list_content_import_sources() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.ai_content.create_content_import_source(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a new content import source by sending a POST request to this endpoint. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.ai_content.create_content_import_source( + url="https://www.example.com", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**url:** `str` — The URL of the content import source. + +
+
+ +
+
+ +**status:** `typing.Optional[CreateContentImportSourceRequestStatus]` — The status of the content import source. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.ai_content.get_content_import_source(...) +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.ai_content.get_content_import_source( + source_id="source_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**source_id:** `str` — The unique identifier for the content import source which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.ai_content.update_content_import_source(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can update an existing content import source. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.ai_content.update_content_import_source( + source_id="source_id", + sync_behavior="api", + url="https://www.example.com", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**source_id:** `str` — The unique identifier for the content import source which is given by Intercom. + +
+
+ +
+
+ +**sync_behavior:** `UpdateContentImportSourceRequestSyncBehavior` — If you intend to create or update External Pages via the API, this should be set to `api`. You can not change the value to or from api. + +
+
+ +
+
+ +**url:** `str` — The URL of the content import source. This may only be different from the existing value if the sync behavior is API. + +
+
+ +
+
+ +**status:** `typing.Optional[UpdateContentImportSourceRequestStatus]` — The status of the content import source. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.ai_content.delete_content_import_source(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can delete a content import source by making a DELETE request this endpoint. This will also delete all external pages that were imported from this source. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.ai_content.delete_content_import_source( + source_id="source_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**source_id:** `str` — The unique identifier for the content import source which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.ai_content.list_external_pages() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can retrieve a list of all external pages for a workspace. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.ai_content.list_external_pages() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.ai_content.create_external_page(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a new external page by sending a POST request to this endpoint. If an external page already exists with the specified source_id and external_id, it will be updated instead. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.ai_content.create_external_page( + title="Test", + html="

Test

", + url="https://www.example.com", + source_id=44, + external_id="abc1234", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**title:** `str` — The title of the external page. + +
+
+ +
+
+ +**html:** `str` — The body of the external page in HTML. + +
+
+ +
+
+ +**source_id:** `int` — The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + +
+
+ +
+
+ +**external_id:** `str` — The identifier for the external page which was given by the source. Must be unique for the source. + +
+
+ +
+
+ +**url:** `typing.Optional[str]` — The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. When a URL is not present, Fin will not reference the source. + +
+
+ +
+
+ +**ai_agent_availability:** `typing.Optional[bool]` — Whether the external page should be used to answer questions by AI Agent. Will not default when updating an existing external page. + +
+
+ +
+
+ +**ai_copilot_availability:** `typing.Optional[bool]` — Whether the external page should be used to answer questions by AI Copilot. Will not default when updating an existing external page. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.ai_content.get_external_page(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can retrieve an external page. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.ai_content.get_external_page( + page_id="page_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**page_id:** `str` — The unique identifier for the external page which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.ai_content.update_external_page(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can update an existing external page (if it was created via the API). +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.ai_content.update_external_page( + page_id="page_id", + title="Test", + html="

Test

", + url="https://www.example.com", + source_id=47, + external_id="5678", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**page_id:** `str` — The unique identifier for the external page which is given by Intercom. + +
+
+ +
+
+ +**title:** `str` — The title of the external page. + +
+
+ +
+
+ +**html:** `str` — The body of the external page in HTML. + +
+
+ +
+
+ +**url:** `str` — The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. + +
+
+ +
+
+ +**source_id:** `int` — The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + +
+
+ +
+
+ +**fin_availability:** `typing.Optional[bool]` — Whether the external page should be used to answer questions by Fin. + +
+
+ +
+
+ +**external_id:** `typing.Optional[str]` — The identifier for the external page which was given by the source. Must be unique for the source. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.ai_content.delete_external_page(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Sending a DELETE request for an external page will remove it from the content library UI and from being used for AI answers. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.ai_content.delete_external_page( + page_id="page_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**page_id:** `str` — The unique identifier for the external page which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Articles +
client.articles.list(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all articles by making a GET request to `https://api.intercom.io/articles`. + +> 📘 How are the articles sorted and ordered? +> +> Articles will be returned in descending order on the `updated_at` attribute. This means if you need to iterate through results then we'll show the most recently updated articles first. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +response = client.articles.list() +for item in response: + yield item +# alternatively, you can paginate page-by-page +for page in response.iter_pages(): + yield page + +``` +
+
+
+
+ +#### ⚙️ 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 + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.articles.create(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a new article by making a POST request to `https://api.intercom.io/articles`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import CreateArticleRequest, Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.articles.create( + request=CreateArticleRequest( + title="Thanks for everything", + description="Description of the Article", + body="Body of the Article", + author_id=1295, + state="published", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `typing.Optional[CreateArticleRequest]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.articles.find(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single article by making a GET request to `https://api.intercom.io/articles/`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.articles.find( + article_id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**article_id:** `int` — The unique identifier for the article which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.articles.update(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can update the details of a single article by making a PUT request to `https://api.intercom.io/articles/`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.articles.update( + article_id=1, + title="Christmas is here!", + body="

New gifts in store for the jolly season

", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**article_id:** `int` — The unique identifier for the article which is given by Intercom. + +
+
+ +
+
+ +**title:** `typing.Optional[str]` — The title of the article.For multilingual articles, this will be the title of the default language's content. + +
+
+ +
+
+ +**description:** `typing.Optional[str]` — The description of the article. For multilingual articles, this will be the description of the default language's content. + +
+
+ +
+
+ +**body:** `typing.Optional[str]` — The content of the article. For multilingual articles, this will be the body of the default language's content. + +
+
+ +
+
+ +**author_id:** `typing.Optional[int]` — The id of the author of the article. For multilingual articles, this will be the id of the author of the default language's content. Must be a teammate on the help center's workspace. + +
+
+ +
+
+ +**state:** `typing.Optional[UpdateArticleRequestState]` — Whether the article will be `published` or will be a `draft`. Defaults to draft. For multilingual articles, this will be the state of the default language's content. + +
+
+ +
+
+ +**parent_id:** `typing.Optional[str]` — The id of the article's parent collection or section. An article without this field stands alone. + +
+
+ +
+
+ +**parent_type:** `typing.Optional[str]` — The type of parent, which can either be a `collection` or `section`. + +
+
+ +
+
+ +**translated_content:** `typing.Optional[ArticleTranslatedContent]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.articles.delete(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can delete a single article by making a DELETE request to `https://api.intercom.io/articles/`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.articles.delete( + article_id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**article_id:** `int` — The unique identifier for the article which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.articles.search(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can search for articles by making a GET request to `https://api.intercom.io/articles/search`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.articles.search( + phrase="Getting started", + state="published", + help_center_id=1, + highlight=True, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**phrase:** `typing.Optional[str]` — The phrase within your articles to search for. + +
+
+ +
+
+ +**state:** `typing.Optional[str]` — The state of the Articles returned. One of `published`, `draft` or `all`. + +
+
+ +
+
+ +**help_center_id:** `typing.Optional[int]` — The ID of the Help Center to search in. + +
+
+ +
+
+ +**highlight:** `typing.Optional[bool]` — Return a highlighted version of the matching content within your articles. Refer to the response schema for more details. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Away Status Reasons +
client.away_status_reasons.list_away_status_reasons() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Returns a list of all away status reasons configured for the workspace, including deleted ones. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.away_status_reasons.list_away_status_reasons() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Export +
client.export.enqueue_a_new_reporting_data_export_job(...) +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.export.enqueue_a_new_reporting_data_export_job( + dataset_id="conversation", + attribute_ids=["conversation_id", "conversation_started_at"], + start_time=1717490000, + end_time=1717510000, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**dataset_id:** `str` + +
+
+ +
+
+ +**attribute_ids:** `typing.Sequence[str]` + +
+
+ +
+
+ +**start_time:** `int` + +
+
+ +
+
+ +**end_time:** `int` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.export.list_available_datasets_and_attributes() +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.export.list_available_datasets_and_attributes() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Data Export +
client.data_export.export_reporting_data(...) +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.data_export.export_reporting_data( + job_identifier="job_identifier", + app_id="app_id", + client_id="client_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**job_identifier:** `str` — Unique identifier of the job. + +
+
+ +
+
+ +**app_id:** `str` — The Intercom defined code of the workspace the company is associated to. + +
+
+ +
+
+ +**client_id:** `str` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.data_export.download_reporting_data_export(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Download the data from a completed reporting data export job. + +> Octet header required +> +> You will have to specify the header Accept: `application/octet-stream` when hitting this endpoint. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.data_export.download_reporting_data_export( + job_identifier="job_identifier", + app_id="app_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**job_identifier:** `str` + +
+
+ +
+
+ +**app_id:** `str` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.data_export.create(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +To create your export job, you need to send a `POST` request to the export endpoint `https://api.intercom.io/export/content/data`. + +The only parameters you need to provide are the range of dates that you want exported. + +>🚧 Limit of one active job +> +> You can only have one active job per workspace. You will receive a HTTP status code of 429 with the message Exceeded rate limit of 1 pending message data export jobs if you attempt to create a second concurrent job. + +>❗️ Updated_at not included +> +> It should be noted that the timeframe only includes messages sent during the time period and not messages that were only updated during this period. For example, if a message was updated yesterday but sent two days ago, you would need to set the created_at_after date before the message was sent to include that in your retrieval job. + +>📘 Date ranges are inclusive +> +> Requesting data for 2018-06-01 until 2018-06-30 will get all data for those days including those specified - e.g. 2018-06-01 00:00:00 until 2018-06-30 23:59:99. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.data_export.create( + created_at_after=1734519776, + created_at_before=1734537776, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**created_at_after:** `int` — The start date that you request data for. It must be formatted as a unix timestamp. + +
+
+ +
+
+ +**created_at_before:** `int` — The end date that you request data for. It must be formatted as a unix timestamp. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.data_export.find(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can view the status of your job by sending a `GET` request to the URL +`https://api.intercom.io/export/content/data/{job_identifier}` - the `{job_identifier}` is the value returned in the response when you first created the export job. More on it can be seen in the Export Job Model. + +> 🚧 Jobs expire after two days +> All jobs that have completed processing (and are thus available to download from the provided URL) will have an expiry limit of two days from when the export ob completed. After this, the data will no longer be available. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.data_export.find( + job_identifier="job_identifier", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**job_identifier:** `str` — job_identifier + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.data_export.cancel(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can cancel your job +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.data_export.cancel( + job_identifier="job_identifier", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**job_identifier:** `str` — job_identifier + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.data_export.download(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +When a job has a status of complete, and thus a filled download_url, you can download your data by hitting that provided URL, formatted like so: https://api.intercom.io/download/content/data/xyz1234. + +Your exported message data will be streamed continuously back down to you in a gzipped CSV format. + +> 📘 Octet header required +> +> You will have to specify the header Accept: `application/octet-stream` when hitting this endpoint. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.data_export.download( + job_identifier="job_identifier", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**job_identifier:** `str` — job_identifier + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## HelpCenters +
client.help_centers.find(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single Help Center by making a GET request to `https://api.intercom.io/help_center/help_center/`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.help_centers.find( + help_center_id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**help_center_id:** `int` — The unique identifier for the collection which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.help_centers.list(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can list all Help Centers by making a GET request to `https://api.intercom.io/help_center/help_centers`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +response = client.help_centers.list() +for item in response: + yield item +# alternatively, you can paginate page-by-page +for page in response.iter_pages(): + yield page + +``` +
+
+
+
+ +#### ⚙️ 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 + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Internal Articles +
client.internal_articles.list_internal_articles() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all internal articles by making a GET request to `https://api.intercom.io/internal_articles`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.internal_articles.list_internal_articles() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.internal_articles.create_internal_article(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a new internal article by making a POST request to `https://api.intercom.io/internal_articles`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import CreateInternalArticleRequest, Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.internal_articles.create_internal_article( + request=CreateInternalArticleRequest( + title="Thanks for everything", + body="Body of the Internal Article", + author_id=1295, + owner_id=1295, + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `typing.Optional[CreateInternalArticleRequest]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.internal_articles.retrieve_internal_article(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single internal article by making a GET request to `https://api.intercom.io/internal_articles/`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.internal_articles.retrieve_internal_article( + internal_article_id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**internal_article_id:** `int` — The unique identifier for the article which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.internal_articles.update_internal_article(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can update the details of a single internal article by making a PUT request to `https://api.intercom.io/internal_articles/`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.internal_articles.update_internal_article( + internal_article_id=1, + title="Christmas is here!", + body="

New gifts in store for the jolly season

", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**internal_article_id:** `int` — The unique identifier for the internal article which is given by Intercom. + +
+
+ +
+
+ +**title:** `typing.Optional[str]` — The title of the article. + +
+
+ +
+
+ +**body:** `typing.Optional[str]` — The content of the article. + +
+
+ +
+
+ +**author_id:** `typing.Optional[int]` — The id of the author of the article. + +
+
+ +
+
+ +**owner_id:** `typing.Optional[int]` — The id of the author of the article. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.internal_articles.delete_internal_article(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can delete a single internal article by making a DELETE request to `https://api.intercom.io/internal_articles/`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.internal_articles.delete_internal_article( + internal_article_id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**internal_article_id:** `int` — The unique identifier for the internal article which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.internal_articles.search_internal_articles(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can search for internal articles by making a GET request to `https://api.intercom.io/internal_articles/search`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.internal_articles.search_internal_articles( + folder_id="folder_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**folder_id:** `typing.Optional[str]` — The ID of the folder to search in. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Companies +
client.companies.retrieve(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a single company by passing in `company_id` or `name`. + + `https://api.intercom.io/companies?name={name}` + + `https://api.intercom.io/companies?company_id={company_id}` + +You can fetch all companies and filter by `segment_id` or `tag_id` as a query parameter. + + `https://api.intercom.io/companies?tag_id={tag_id}` + + `https://api.intercom.io/companies?segment_id={segment_id}` +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.companies.retrieve( + name="my company", + company_id="12345", + tag_id="678910", + segment_id="98765", + page=1, + per_page=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**name:** `typing.Optional[str]` — The `name` of the company to filter by. + +
+
+ +
+
+ +**company_id:** `typing.Optional[str]` — The `company_id` of the company to filter by. + +
+
+ +
+
+ +**tag_id:** `typing.Optional[str]` — The `tag_id` of the company to filter by. + +
+
+ +
+
+ +**segment_id:** `typing.Optional[str]` — The `segment_id` of the company to filter by. + +
+
+ +
+
+ +**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. + +
+
+
+
+ + +
+
+
+ +
client.companies.create_or_update(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create or update a company. + +Companies will be only visible in Intercom when there is at least one associated user. + +Companies are looked up via `company_id` in a `POST` request, if not found via `company_id`, the new company will be created, if found, that company will be updated. + +{% admonition type="warning" name="Using `company_id`" %} + You can set a unique `company_id` value when creating a company. However, it is not possible to update `company_id`. Be sure to set a unique value once upon creation of the company. +{% /admonition %} +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import CreateOrUpdateCompanyRequest, Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.companies.create_or_update( + request=CreateOrUpdateCompanyRequest(), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `typing.Optional[CreateOrUpdateCompanyRequest]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.companies.find(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a single company. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.companies.find( + company_id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**company_id:** `str` — The unique identifier for the company which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.companies.update(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can update a single company using the Intercom provisioned `id`. + +{% admonition type="warning" name="Using `company_id`" %} + When updating a company it is not possible to update `company_id`. This can only be set once upon creation of the company. +{% /admonition %} +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom, UpdateCompanyRequestBody + +client = Intercom( + token="YOUR_TOKEN", +) +client.companies.update( + company_id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + request=UpdateCompanyRequestBody(), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**company_id:** `str` — The unique identifier for the company which is given by Intercom + +
+
+ +
+
+ +**request:** `typing.Optional[UpdateCompanyRequestBody]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.companies.delete(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can delete a single company. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.companies.delete( + company_id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**company_id:** `str` — The unique identifier for the company which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.companies.list_attached_contacts(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all contacts that belong to a company. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.companies.list_attached_contacts( + company_id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**company_id:** `str` — The unique identifier for the company which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.companies.list_attached_segments(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all segments that belong to a company. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.companies.list_attached_segments( + company_id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**company_id:** `str` — The unique identifier for the company which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.companies.list(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can list companies. The company list is sorted by the `last_request_at` field and by default is ordered descending, most recently requested first. + +Note that the API does not include companies who have no associated users in list responses. + +When using the Companies endpoint and the pages object to iterate through the returned companies, there is a limit of 10,000 Companies that can be returned. If you need to list or iterate on more than 10,000 Companies, please use the [Scroll API](https://developers.intercom.com/reference#iterating-over-all-companies). +{% admonition type="warning" name="Pagination" %} + You can use pagination to limit the number of results returned. The default is `20` 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 %} +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +response = client.companies.list( + page=1, + per_page=1, + order="desc", +) +for item in response: + yield item +# alternatively, you can paginate page-by-page +for page in response.iter_pages(): + yield page + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**page:** `typing.Optional[int]` — The page of results to fetch. Defaults to first page + +
+
+ +
+
+ +**per_page:** `typing.Optional[int]` — How many results to return per page. Defaults to 15 + +
+
+ +
+
+ +**order:** `typing.Optional[str]` — `asc` or `desc`. Return the companies in ascending or descending order. Defaults to desc + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.companies.scroll(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ + The `list all companies` functionality does not work well for huge datasets, and can result in errors and performance problems when paging deeply. The Scroll API provides an efficient mechanism for iterating over all companies in a dataset. + +- Each app can only have 1 scroll open at a time. You'll get an error message if you try to have more than one open per app. +- If the scroll isn't used for 1 minute, it expires and calls with that scroll param will fail +- If the end of the scroll is reached, "companies" will be empty and the scroll parameter will expire + +{% admonition type="info" name="Scroll Parameter" %} + You can get the first page of companies by simply sending a GET request to the scroll endpoint. + For subsequent requests you will need to use the scroll parameter from the response. +{% /admonition %} +{% admonition type="danger" name="Scroll network timeouts" %} + Since scroll is often used on large datasets network errors such as timeouts can be encountered. When this occurs you will see a HTTP 500 error with the following message: + "Request failed due to an internal network error. Please restart the scroll operation." + If this happens, you will need to restart your scroll query: It is not possible to continue from a specific point when using scroll. +{% /admonition %} +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +response = client.companies.scroll( + scroll_param="scroll_param", +) +for item in response: + yield item +# alternatively, you can paginate page-by-page +for page in response.iter_pages(): + yield page + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**scroll_param:** `typing.Optional[str]` — + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.companies.attach_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can attach a company to a single contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.companies.attach_contact( + contact_id="contact_id", + company_id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — The unique identifier for the contact which is given by Intercom + +
+
+ +
+
+ +**company_id:** `str` — The unique identifier for the company which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.companies.detach_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can detach a company from a single contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.companies.detach_contact( + contact_id="58a430d35458202d41b1e65b", + company_id="58a430d35458202d41b1e65b", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — The unique identifier for the contact which is given by Intercom + +
+
+ +
+
+ +**company_id:** `str` — The unique identifier for the company which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Contacts +
client.contacts.list_attached_companies(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of companies that are associated to a contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +response = client.contacts.list_attached_companies( + contact_id="63a07ddf05a32042dffac965", + page=1, + per_page=1, +) +for item in response: + yield item +# alternatively, you can paginate page-by-page +for page in response.iter_pages(): + yield page + +``` +
+
+
+
+ +#### ⚙️ 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. + +
+
+
+
+ + +
+
+
+ +
client.contacts.list_attached_segments(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of segments that are associated to a contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.contacts.list_attached_segments( + contact_id="63a07ddf05a32042dffac965", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — The unique identifier for the contact which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.contacts.list_attached_subscriptions(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.contacts.list_attached_subscriptions( + contact_id="63a07ddf05a32042dffac965", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — The unique identifier for the contact which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.contacts.attach_subscription(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.contacts.attach_subscription( + contact_id="63a07ddf05a32042dffac965", + subscription_id="invalid_id", + consent_type="opt_in", +) + +``` +
+
+
+
+ +#### ⚙️ 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. + +
+
+
+
+ + +
+
+
+ +
client.contacts.detach_subscription(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.contacts.detach_subscription( + contact_id="63a07ddf05a32042dffac965", + subscription_id="37846", +) + +``` +
+
+
+
+ +#### ⚙️ 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. + +
+
+
+
+ + +
+
+
+ +
client.contacts.list_attached_tags(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all tags that are attached to a specific contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.contacts.list_attached_tags( + contact_id="63a07ddf05a32042dffac965", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — The unique identifier for the contact which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.contacts.find(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.contacts.find( + contact_id="63a07ddf05a32042dffac965", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — contact_id + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.contacts.update(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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 %} +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.contacts.update( + contact_id="63a07ddf05a32042dffac965", + custom_attributes={"order": ["21"]}, +) + +``` +
+
+
+
+ +#### ⚙️ 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. + +
+
+
+
+ + +
+
+
+ +
client.contacts.delete(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can delete a single contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.contacts.delete( + contact_id="contact_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — contact_id + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.contacts.merge_lead_in_user(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can merge a contact with a `role` of `lead` into a contact with a `role` of `user`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.contacts.merge_lead_in_user( + lead_id="6762f0d51bb69f9f2193bb7f", + contact_id="6762f0d51bb69f9f2193bb80", +) + +``` +
+
+
+
+ +#### ⚙️ 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. + +
+
+
+
+ + +
+
+
+ +
client.contacts.search(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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
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
Shortcut for `OR` queries
Values must be in Array | +| NIN | All | Not In
Shortcut for `OR !` queries
Values must be in Array | +| > | Integer
Date (UNIX Timestamp) | Greater than | +| < | Integer
Date (UNIX Timestamp) | Lower than | +| ~ | String | Contains | +| !~ | String | Doesn't Contain | +| ^ | String | Starts With | +| $ | String | Ends With | +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import ( + Intercom, + MultipleFilterSearchRequest, + SingleFilterSearchRequest, + StartingAfterPaging, +) + +client = Intercom( + token="YOUR_TOKEN", +) +response = client.contacts.search( + query=MultipleFilterSearchRequest( + operator="AND", + value=[ + SingleFilterSearchRequest( + field="created_at", + operator=">", + value="1306054154", + ) + ], + ), + pagination=StartingAfterPaging( + per_page=5, + ), +) +for item in response: + yield item +# alternatively, you can paginate page-by-page +for page in response.iter_pages(): + yield page + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**query:** `SearchRequestQuery` + +
+
+ +
+
+ +**pagination:** `typing.Optional[StartingAfterPaging]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.contacts.list(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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 %} +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +response = client.contacts.list() +for item in response: + yield item +# alternatively, you can paginate page-by-page +for page in response.iter_pages(): + yield page + +``` +
+
+
+
+ +#### ⚙️ 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. + +
+
+
+
+ + +
+
+
+ +
client.contacts.create(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a new contact (ie. user or lead). +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import CreateContactRequestWithEmail, Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.contacts.create( + request=CreateContactRequestWithEmail( + email="joebloggs@intercom.io", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `CreateContactRequest` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.contacts.show_contact_by_external_id(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single contact by external ID. Note that this endpoint only supports users and not leads. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.contacts.show_contact_by_external_id( + external_id="cdd29344-5e0c-4ef0-ac56-f9ba2979bc27", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**external_id:** `str` — The external ID of the user that you want to retrieve + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.contacts.archive(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can archive a single contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.contacts.archive( + contact_id="63a07ddf05a32042dffac965", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — contact_id + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.contacts.unarchive(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can unarchive a single contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.contacts.unarchive( + contact_id="63a07ddf05a32042dffac965", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — contact_id + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.contacts.block_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Block a single contact.
**Note:** conversations of the contact will also be archived during the process.
More details in [FAQ How do I block Inbox spam?](https://www.intercom.com/help/en/articles/8838656-inbox-faqs) +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.contacts.block_contact( + contact_id="63a07ddf05a32042dffac965", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — contact_id + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Notes +
client.notes.list(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of notes that are associated to a contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +response = client.notes.list( + contact_id="contact_id", +) +for item in response: + yield item +# alternatively, you can paginate page-by-page +for page in response.iter_pages(): + yield page + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — The unique identifier of a contact. + +
+
+ +
+
+ +**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. + +
+
+
+
+ + +
+
+
+ +
client.notes.create(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can add a note to a single contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.notes.create( + contact_id="123", + body="Hello", + admin_id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — The unique identifier of a given contact. + +
+
+ +
+
+ +**body:** `str` — The text of the note. + +
+
+ +
+
+ +**admin_id:** `typing.Optional[str]` — The unique identifier of a given admin. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.notes.find(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single note. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.notes.find( + note_id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**note_id:** `int` — The unique identifier of a given note + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Tags +
client.tags.tag_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can tag a specific contact. This will return a tag object for the tag that was added to the contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.tags.tag_contact( + contact_id="63a07ddf05a32042dffac965", + tag_id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — The unique identifier for the contact which is given by Intercom + +
+
+ +
+
+ +**tag_id:** `str` — The unique identifier for the tag which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.tags.untag_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can remove tag from a specific contact. This will return a tag object for the tag that was removed from the contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.tags.untag_contact( + contact_id="63a07ddf05a32042dffac965", + tag_id="7522907", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — The unique identifier for the contact which is given by Intercom + +
+
+ +
+
+ +**tag_id:** `str` — The unique identifier for the tag which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.tags.tag_conversation(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can tag a specific conversation. This will return a tag object for the tag that was added to the conversation. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.tags.tag_conversation( + conversation_id="64619700005694", + tag_id="7522907", + admin_id="780", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**conversation_id:** `str` — conversation_id + +
+
+ +
+
+ +**tag_id:** `str` — The unique identifier for the tag which is given by Intercom + +
+
+ +
+
+ +**admin_id:** `str` — The unique identifier for the admin which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.tags.untag_conversation(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can remove tag from a specific conversation. This will return a tag object for the tag that was removed from the conversation. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.tags.untag_conversation( + conversation_id="64619700005694", + tag_id="7522907", + admin_id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**conversation_id:** `str` — conversation_id + +
+
+ +
+
+ +**tag_id:** `str` — tag_id + +
+
+ +
+
+ +**admin_id:** `str` — The unique identifier for the admin which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.tags.list() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all tags for a given workspace. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.tags.list() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.tags.create(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can use this endpoint to perform the following operations: + + **1. Create a new tag:** You can create a new tag by passing in the tag name as specified in "Create or Update Tag Request Payload" described below. + + **2. Update an existing tag:** You can update an existing tag by passing the id of the tag as specified in "Create or Update Tag Request Payload" described below. + + **3. Tag Companies:** You can tag single company or a list of companies. You can tag a company by passing in the tag name and the company details as specified in "Tag Company Request Payload" described below. Also, if the tag doesn't exist then a new one will be created automatically. + + **4. Untag Companies:** You can untag a single company or a list of companies. You can untag a company by passing in the tag id and the company details as specified in "Untag Company Request Payload" described below. + + **5. Tag Multiple Users:** You can tag a list of users. You can tag the users by passing in the tag name and the user details as specified in "Tag Users Request Payload" described below. + +Each operation will return a tag object. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import ( + Intercom, + TagMultipleUsersRequest, + TagMultipleUsersRequestUsersItem, +) + +client = Intercom( + token="YOUR_TOKEN", +) +client.tags.create( + request=TagMultipleUsersRequest( + name="test", + users=[ + TagMultipleUsersRequestUsersItem( + id="123", + ) + ], + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `TagsCreateRequestBody` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.tags.find(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of tags that are on the workspace by their id. +This will return a tag object. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.tags.find( + tag_id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**tag_id:** `str` — The unique identifier of a given tag + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.tags.delete(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can delete the details of tags that are on the workspace by passing in the id. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.tags.delete( + tag_id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**tag_id:** `str` — The unique identifier of a given tag + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.tags.tag_ticket(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can tag a specific ticket. This will return a tag object for the tag that was added to the ticket. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.tags.tag_ticket( + ticket_id="64619700005694", + tag_id="7522907", + admin_id="780", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**ticket_id:** `str` — ticket_id + +
+
+ +
+
+ +**tag_id:** `str` — The unique identifier for the tag which is given by Intercom + +
+
+ +
+
+ +**admin_id:** `str` — The unique identifier for the admin which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.tags.untag_ticket(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can remove tag from a specific ticket. This will return a tag object for the tag that was removed from the ticket. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.tags.untag_ticket( + ticket_id="64619700005694", + tag_id="7522907", + admin_id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**ticket_id:** `str` — ticket_id + +
+
+ +
+
+ +**tag_id:** `str` — The unique identifier for the tag which is given by Intercom + +
+
+ +
+
+ +**admin_id:** `str` — The unique identifier for the admin which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Conversations +
client.conversations.list(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all conversations. + +You can optionally request the result page size and the cursor to start after to fetch the result. +{% admonition type="warning" name="Pagination" %} + You can use pagination to limit the number of results returned. The default is `20` 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 %} +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +response = client.conversations.list( + per_page=1, + starting_after="starting_after", +) +for item in response: + yield item +# alternatively, you can paginate page-by-page +for page in response.iter_pages(): + yield page + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**per_page:** `typing.Optional[int]` — How many results per page + +
+
+ +
+
+ +**starting_after:** `typing.Optional[str]` — String used to get the next page of conversations. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.conversations.create(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a conversation that has been initiated by a contact (ie. user or lead). +The conversation can be an in-app message only. + +{% admonition type="info" name="Sending for visitors" %} +You can also send a message from a visitor by specifying their `user_id` or `id` value in the `from` field, along with a `type` field value of `contact`. +This visitor will be automatically converted to a contact with a lead role once the conversation is created. +{% /admonition %} + +This will return the Message model that has been created. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.conversations import CreateConversationRequestFrom + +client = Intercom( + token="YOUR_TOKEN", +) +client.conversations.create( + from_=CreateConversationRequestFrom( + type="user", + id="123_doesnt_exist", + ), + body="Hello there", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**from_:** `CreateConversationRequestFrom` + +
+
+ +
+
+ +**body:** `str` — The content of the message. HTML is not supported. + +
+
+ +
+
+ +**created_at:** `typing.Optional[int]` — The time the conversation was created as a UTC Unix timestamp. If not provided, the current time will be used. This field is only recommneded for migrating past conversations from another source into Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.conversations.find(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ + +You can fetch the details of a single conversation. + +This will return a single Conversation model with all its conversation parts. + +{% admonition type="warning" name="Hard limit of 500 parts" %} +The maximum number of conversation parts that can be returned via the API is 500. If you have more than that we will return the 500 most recent conversation parts. +{% /admonition %} + +For AI agent conversation metadata, please note that you need to have the agent enabled in your workspace, which is a [paid feature](https://www.intercom.com/help/en/articles/8205718-fin-resolutions#h_97f8c2e671). +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.conversations.find( + conversation_id="123", + display_as="plaintext", + include_translations=True, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**conversation_id:** `str` — The id of the conversation to target + +
+
+ +
+
+ +**display_as:** `typing.Optional[str]` — Set to plaintext to retrieve conversation messages in plain text. + +
+
+ +
+
+ +**include_translations:** `typing.Optional[bool]` — If set to true, conversation parts will be translated to the detected language of the conversation. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.conversations.update(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ + +You can update an existing conversation. + +{% admonition type="info" name="Replying and other actions" %} +If you want to reply to a coveration or take an action such as assign, unassign, open, close or snooze, take a look at the reply and manage endpoints. +{% /admonition %} + +{% admonition type="info" %} + This endpoint handles both **conversation updates** and **custom object associations**. + + See _`update a conversation with an association to a custom object instance`_ in the request/response examples to see the custom object association format. +{% /admonition %} + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.conversations.update( + conversation_id="conversation_id", + display_as="plaintext", + read=True, + title="new conversation title", + custom_attributes={"issue_type": "Billing", "priority": "High"}, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**conversation_id:** `str` — The id of the conversation to target + +
+
+ +
+
+ +**display_as:** `typing.Optional[str]` — Set to plaintext to retrieve conversation messages in plain text. + +
+
+ +
+
+ +**read:** `typing.Optional[bool]` — Mark a conversation as read within Intercom. + +
+
+ +
+
+ +**title:** `typing.Optional[str]` — The title given to the conversation + +
+
+ +
+
+ +**custom_attributes:** `typing.Optional[CustomAttributes]` + +
+
+ +
+
+ +**company_id:** `typing.Optional[str]` — The ID of the company that the conversation is associated with. The unique identifier for the company which is given by Intercom. Set to nil to remove company. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.conversations.delete_conversation(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can delete a single conversation. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.conversations.delete_conversation( + conversation_id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**conversation_id:** `int` — id + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.conversations.search(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can search for multiple conversations by the value of their attributes in order to fetch exactly which ones you want. + +To search for conversations, you need to send a `POST` request to `https://api.intercom.io/conversations/search`. + +This will accept a query object in the body which will define your filters in order to search for conversations. +{% 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 `20` results per page and maximum is `150`. + 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 %} + +### 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 + +### Accepted Fields + +Most keys listed in the conversation 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"`). +The `source.body` field is unique as the search will not be performed against the entire value, but instead against every element of the value separately. For example, when searching for a conversation with a `"I need support"` body - the query should contain a `=` operator with the value `"support"` for such conversation to be returned. A query with a `=` operator and a `"need support"` value will not yield a result. + +| Field | Type | +| :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | +| id | String | +| created_at | Date (UNIX timestamp) | +| updated_at | Date (UNIX timestamp) | +| source.type | String
Accepted fields are `conversation`, `email`, `facebook`, `instagram`, `phone_call`, `phone_switch`, `push`, `sms`, `twitter` and `whatsapp`. | +| source.id | String | +| source.delivered_as | String | +| source.subject | String | +| source.body | String | +| source.author.id | String | +| source.author.type | String | +| source.author.name | String | +| source.author.email | String | +| source.url | String | +| contact_ids | String | +| teammate_ids | String | +| admin_assignee_id | String | +| team_assignee_id | String | +| channel_initiated | String | +| open | Boolean | +| read | Boolean | +| state | String | +| waiting_since | Date (UNIX timestamp) | +| snoozed_until | Date (UNIX timestamp) | +| tag_ids | String | +| priority | String | +| statistics.time_to_assignment | Integer | +| statistics.time_to_admin_reply | Integer | +| statistics.time_to_first_close | Integer | +| statistics.time_to_last_close | Integer | +| statistics.median_time_to_reply | Integer | +| statistics.first_contact_reply_at | Date (UNIX timestamp) | +| statistics.first_assignment_at | Date (UNIX timestamp) | +| statistics.first_admin_reply_at | Date (UNIX timestamp) | +| statistics.first_close_at | Date (UNIX timestamp) | +| statistics.last_assignment_at | Date (UNIX timestamp) | +| statistics.last_assignment_admin_reply_at | Date (UNIX timestamp) | +| statistics.last_contact_reply_at | Date (UNIX timestamp) | +| statistics.last_admin_reply_at | Date (UNIX timestamp) | +| statistics.last_close_at | Date (UNIX timestamp) | +| statistics.last_closed_by_id | String | +| statistics.count_reopens | Integer | +| statistics.count_assignments | Integer | +| statistics.count_conversation_parts | Integer | +| conversation_rating.requested_at | Date (UNIX timestamp) | +| conversation_rating.replied_at | Date (UNIX timestamp) | +| conversation_rating.score | Integer | +| conversation_rating.remark | String | +| conversation_rating.contact_id | String | +| conversation_rating.admin_d | String | +| ai_agent_participated | Boolean | +| ai_agent.resolution_state | String | +| ai_agent.last_answer_type | String | +| ai_agent.rating | Integer | +| ai_agent.rating_remark | String | +| ai_agent.source_type | String | +| ai_agent.source_title | String | + +### Accepted Operators + +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 Shortcut for `OR` queries Values most be in Array | +| NIN | All | Not In Shortcut for `OR !` queries Values must be in Array | +| > | Integer Date (UNIX Timestamp) | Greater (or equal) than | +| < | Integer Date (UNIX Timestamp) | Lower (or equal) than | +| ~ | String | Contains | +| !~ | String | Doesn't Contain | +| ^ | String | Starts With | +| $ | String | Ends With | +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import ( + Intercom, + MultipleFilterSearchRequest, + SingleFilterSearchRequest, + StartingAfterPaging, +) + +client = Intercom( + token="YOUR_TOKEN", +) +response = client.conversations.search( + query=MultipleFilterSearchRequest( + operator="AND", + value=[ + SingleFilterSearchRequest( + field="created_at", + operator=">", + value="1306054154", + ) + ], + ), + pagination=StartingAfterPaging( + per_page=5, + ), +) +for item in response: + yield item +# alternatively, you can paginate page-by-page +for page in response.iter_pages(): + yield page + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**query:** `SearchRequestQuery` + +
+
+ +
+
+ +**pagination:** `typing.Optional[StartingAfterPaging]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.conversations.reply(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can reply to a conversation with a message from an admin or on behalf of a contact, or with a note for admins. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import ContactReplyIntercomUserIdRequest, Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.conversations.reply( + conversation_id='123 or "last"', + request=ContactReplyIntercomUserIdRequest( + body="Thanks again :)", + intercom_user_id="6762f1571bb69f9f2193bbbb", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**conversation_id:** `str` — The Intercom provisioned identifier for the conversation or the string "last" to reply to the last part of the conversation + +
+
+ +
+
+ +**request:** `ReplyConversationRequest` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.conversations.manage(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +For managing conversations you can: +- Close a conversation +- Snooze a conversation to reopen on a future date +- Open a conversation which is `snoozed` or `closed` +- Assign a conversation to an admin and/or team. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.conversations import ConversationsManageRequestBody_Close + +client = Intercom( + token="YOUR_TOKEN", +) +client.conversations.manage( + conversation_id="123", + request=ConversationsManageRequestBody_Close( + admin_id="12345", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**conversation_id:** `str` — The identifier for the conversation as given by Intercom. + +
+
+ +
+
+ +**request:** `ConversationsManageRequestBody` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.conversations.attach_contact_as_admin(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can add participants who are contacts to a conversation, on behalf of either another contact or an admin. + +{% admonition type="warning" name="Contacts without an email" %} +If you add a contact via the email parameter and there is no user/lead found on that workspace with he given email, then we will create a new contact with `role` set to `lead`. +{% /admonition %} + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.conversations import ( + AttachContactToConversationRequestCustomerIntercomUserId, +) + +client = Intercom( + token="YOUR_TOKEN", +) +client.conversations.attach_contact_as_admin( + conversation_id="123", + admin_id="12345", + customer=AttachContactToConversationRequestCustomerIntercomUserId( + intercom_user_id="6762f19e1bb69f9f2193bbd5", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**conversation_id:** `str` — The identifier for the conversation as given by Intercom. + +
+
+ +
+
+ +**admin_id:** `typing.Optional[str]` — The `id` of the admin who is adding the new participant. + +
+
+ +
+
+ +**customer:** `typing.Optional[AttachContactToConversationRequestCustomer]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.conversations.detach_contact_as_admin(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can add participants who are contacts to a conversation, on behalf of either another contact or an admin. + +{% admonition type="warning" name="Contacts without an email" %} +If you add a contact via the email parameter and there is no user/lead found on that workspace with he given email, then we will create a new contact with `role` set to `lead`. +{% /admonition %} + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.conversations.detach_contact_as_admin( + conversation_id="123", + contact_id="123", + admin_id="5017690", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**conversation_id:** `str` — The identifier for the conversation as given by Intercom. + +
+
+ +
+
+ +**contact_id:** `str` — The identifier for the contact as given by Intercom. + +
+
+ +
+
+ +**admin_id:** `str` — The `id` of the admin who is performing the action. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.conversations.redact_conversation_part(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can redact a conversation part or the source message of a conversation (as seen in the source object). + +{% admonition type="info" name="Redacting parts and messages" %} +If you are redacting a conversation part, it must have a `body`. If you are redacting a source message, it must have been created by a contact. We will return a `conversation_part_not_redactable` error if these criteria are not met. +{% /admonition %} + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom, RedactConversationRequest_ConversationPart + +client = Intercom( + token="YOUR_TOKEN", +) +client.conversations.redact_conversation_part( + request=RedactConversationRequest_ConversationPart( + conversation_id="really_123_doesnt_exist", + conversation_part_id="really_123_doesnt_exist", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `RedactConversationRequest` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.conversations.convert_to_ticket(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can convert a conversation to a ticket. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.conversations.convert_to_ticket( + conversation_id=1, + ticket_type_id="54", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**conversation_id:** `int` — The id of the conversation to target + +
+
+ +
+
+ +**ticket_type_id:** `str` — The ID of the type of ticket you want to convert the conversation to + +
+
+ +
+
+ +**attributes:** `typing.Optional[TicketRequestCustomAttributes]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.conversations.run_assignment_rules(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +{% admonition type="danger" name="Deprecation of Run Assignment Rules" %} +Run assignment rules is now deprecated in version 2.12 and future versions and will be permanently removed on December 31, 2026. After this date, any requests made to this endpoint will fail. +{% /admonition %} +You can let a conversation be automatically assigned following assignment rules. +{% admonition type="warning" name="When using workflows" %} +It is not possible to use this endpoint with Workflows. +{% /admonition %} +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.conversations.run_assignment_rules( + conversation_id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**conversation_id:** `str` — The identifier for the conversation as given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Custom Channel Events +
client.custom_channel_events.notify_new_conversation(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Notifies Intercom that a new conversation was created in your custom channel/platform. This triggers conversation creation and workflow automations within Intercom for your custom channel integration. +> **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import CustomChannelContact, Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.custom_channel_events.notify_new_conversation( + event_id="event_id", + external_conversation_id="external_conversation_id", + contact=CustomChannelContact( + type="user", + external_id="external_id", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**event_id:** `str` — Unique identifier for the event. + +
+
+ +
+
+ +**external_conversation_id:** `str` — Identifier for the conversation in your application. + +
+
+ +
+
+ +**contact:** `CustomChannelContact` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.custom_channel_events.notify_new_message(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Notifies Intercom that a new message was sent in a conversation on your custom channel/platform. This allows Intercom to process the message and trigger any relevant workflow automations. +> **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import CustomChannelContact, Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.custom_channel_events.notify_new_message( + body="body", + event_id="event_id", + external_conversation_id="external_conversation_id", + contact=CustomChannelContact( + type="user", + external_id="external_id", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**body:** `str` — The message content sent by the user. + +
+
+ +
+
+ +**event_id:** `str` — Unique identifier for the event. + +
+
+ +
+
+ +**external_conversation_id:** `str` — Identifier for the conversation in your application. + +
+
+ +
+
+ +**contact:** `CustomChannelContact` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.custom_channel_events.notify_quick_reply_selected(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Notifies Intercom that a user selected a quick reply option in your custom channel/platform. This allows Intercom to process the response and trigger any relevant workflow automations. +> **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import CustomChannelContact, Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.custom_channel_events.notify_quick_reply_selected( + event_id="evt_67890", + external_conversation_id="conv_13579", + contact=CustomChannelContact( + type="user", + external_id="user_003", + name="Alice Example", + email="alice@example.com", + ), + quick_reply_option_id="1234", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**quick_reply_option_id:** `str` — Id of the selected quick reply option. + +
+
+ +
+
+ +**event_id:** `str` — Unique identifier for the event. + +
+
+ +
+
+ +**external_conversation_id:** `str` — Identifier for the conversation in your application. + +
+
+ +
+
+ +**contact:** `CustomChannelContact` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.custom_channel_events.notify_attribute_collected(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Notifies Intercom that a user provided a response to an attribute collector in your custom channel/platform. This allows Intercom to process the attribute and trigger any relevant workflow automations. +> **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import CustomChannelAttribute, CustomChannelContact, Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.custom_channel_events.notify_attribute_collected( + attribute=CustomChannelAttribute( + id="id", + value="value", + ), + event_id="event_id", + external_conversation_id="external_conversation_id", + contact=CustomChannelContact( + type="user", + external_id="external_id", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**attribute:** `CustomChannelAttribute` + +
+
+ +
+
+ +**event_id:** `str` — Unique identifier for the event. + +
+
+ +
+
+ +**external_conversation_id:** `str` — Identifier for the conversation in your application. + +
+
+ +
+
+ +**contact:** `CustomChannelContact` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Custom Object Instances +
client.custom_object_instances.get_custom_object_instances_by_external_id(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Fetch a Custom Object Instance by external_id. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.custom_object_instances.get_custom_object_instances_by_external_id( + custom_object_type_identifier="Order", + external_id="external_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**custom_object_type_identifier:** `str` — The unique identifier of the custom object type that defines the structure of the custom object instance. + +
+
+ +
+
+ +**external_id:** `str` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.custom_object_instances.create_custom_object_instances(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Create or update a custom object instance +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.custom_object_instances.create_custom_object_instances( + custom_object_type_identifier="Order", + external_id="123", + external_created_at=1392036272, + external_updated_at=1392036272, + custom_attributes={ + "order_number": "ORDER-12345", + "total_amount": "custom_attributes", + }, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**custom_object_type_identifier:** `str` — The unique identifier of the custom object type that defines the structure of the custom object instance. + +
+
+ +
+
+ +**external_id:** `typing.Optional[str]` — A unique identifier for the Custom Object instance in the external system it originated from. + +
+
+ +
+
+ +**external_created_at:** `typing.Optional[int]` — The time when the Custom Object instance was created in the external system it originated from. + +
+
+ +
+
+ +**external_updated_at:** `typing.Optional[int]` — The time when the Custom Object instance was last updated in the external system it originated from. + +
+
+ +
+
+ +**custom_attributes:** `typing.Optional[typing.Dict[str, typing.Optional[str]]]` — The custom attributes which are set for the Custom Object instance. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.custom_object_instances.delete_custom_object_instances_by_id(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Delete a single Custom Object instance by external_id. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.custom_object_instances.delete_custom_object_instances_by_id( + custom_object_type_identifier="Order", + external_id="external_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**custom_object_type_identifier:** `str` — The unique identifier of the custom object type that defines the structure of the custom object instance. + +
+
+ +
+
+ +**external_id:** `str` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.custom_object_instances.get_custom_object_instances_by_id(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Fetch a Custom Object Instance by id. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.custom_object_instances.get_custom_object_instances_by_id( + custom_object_type_identifier="Order", + custom_object_instance_id="custom_object_instance_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**custom_object_type_identifier:** `str` — The unique identifier of the custom object type that defines the structure of the custom object instance. + +
+
+ +
+
+ +**custom_object_instance_id:** `str` — The id or external_id of the custom object instance + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.custom_object_instances.delete_custom_object_instances_by_external_id(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Delete a single Custom Object instance using the Intercom defined id. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.custom_object_instances.delete_custom_object_instances_by_external_id( + custom_object_type_identifier="Order", + custom_object_instance_id="custom_object_instance_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**custom_object_type_identifier:** `str` — The unique identifier of the custom object type that defines the structure of the custom object instance. + +
+
+ +
+
+ +**custom_object_instance_id:** `str` — The Intercom defined id of the custom object instance + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Data Attributes +
client.data_attributes.list(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all data attributes belonging to a workspace for contacts, companies or conversations. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.data_attributes.list( + model="contact", + include_archived=True, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**model:** `typing.Optional[DataAttributesListRequestModel]` — Specify the data attribute model to return. + +
+
+ +
+
+ +**include_archived:** `typing.Optional[bool]` — Include archived attributes in the list. By default we return only non archived data attributes. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.data_attributes.create(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a data attributes for a `contact` or a `company`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import ( + CreateDataAttributeRequestOptions, + CreateDataAttributeRequestOptionsOptionsItem, + Intercom, +) + +client = Intercom( + token="YOUR_TOKEN", +) +client.data_attributes.create( + request=CreateDataAttributeRequestOptions( + options=[ + CreateDataAttributeRequestOptionsOptionsItem( + value="1-10", + ) + ], + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `CreateDataAttributeRequest` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.data_attributes.update(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ + +You can update a data attribute. + +> 🚧 Updating the data type is not possible +> +> It is currently a dangerous action to execute changing a data attribute's type via the API. You will need to update the type via the UI instead. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.data_attributes.update( + data_attribute_id=1, + request={"description": "Trying to archieve", "archived": True}, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**data_attribute_id:** `int` — The data attribute id + +
+
+ +
+
+ +**request:** `UpdateDataAttributeRequestBody` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Events +
client.events.list(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ + +> 🚧 +> +> Please note that you can only 'list' events that are less than 90 days old. Event counts and summaries will still include your events older than 90 days but you cannot 'list' these events individually if they are older than 90 days + +The events belonging to a customer can be listed by sending a GET request to `https://api.intercom.io/events` with a user or lead identifier along with a `type` parameter. The identifier parameter can be one of `user_id`, `email` or `intercom_user_id`. The `type` parameter value must be `user`. + +- `https://api.intercom.io/events?type=user&user_id={user_id}` +- `https://api.intercom.io/events?type=user&email={email}` +- `https://api.intercom.io/events?type=user&intercom_user_id={id}` (this call can be used to list leads) + +The `email` parameter value should be [url encoded](http://en.wikipedia.org/wiki/Percent-encoding) when sending. + +You can optionally define the result page size as well with the `per_page` parameter. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.events.list( + user_id="user_id", + intercom_user_id="intercom_user_id", + email="email", + type="type", + summary=True, + per_page=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**type:** `str` — The value must be user + +
+
+ +
+
+ +**user_id:** `typing.Optional[str]` — user_id query parameter + +
+
+ +
+
+ +**intercom_user_id:** `typing.Optional[str]` — intercom_user_id query parameter + +
+
+ +
+
+ +**email:** `typing.Optional[str]` — email query parameter + +
+
+ +
+
+ +**summary:** `typing.Optional[bool]` — summary flag + +
+
+ +
+
+ +**per_page:** `typing.Optional[int]` — How many results to display per page. Defaults to 15 + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.events.create(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ + +You will need an Access Token that has write permissions to send Events. Once you have a key you can submit events via POST to the Events resource, which is located at https://api.intercom.io/events, or you can send events using one of the client libraries. When working with the HTTP API directly a client should send the event with a `Content-Type` of `application/json`. + +When using the JavaScript API, [adding the code to your app](http://docs.intercom.io/configuring-Intercom/tracking-user-events-in-your-app) makes the Events API available. Once added, you can submit an event using the `trackEvent` method. This will associate the event with the Lead or currently logged-in user or logged-out visitor/lead and send it to Intercom. The final parameter is a map that can be used to send optional metadata about the event. + +With the Ruby client you pass a hash describing the event to `Intercom::Event.create`, or call the `track_user` method directly on the current user object (e.g. `user.track_event`). + +**NB: For the JSON object types, please note that we do not currently support nested JSON structure.** + +| Type | Description | Example | +| :-------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------- | +| String | The value is a JSON String | `"source":"desktop"` | +| Number | The value is a JSON Number | `"load": 3.67` | +| Date | The key ends with the String `_date` and the value is a [Unix timestamp](http://en.wikipedia.org/wiki/Unix_time), assumed to be in the [UTC](http://en.wikipedia.org/wiki/Coordinated_Universal_Time) timezone. | `"contact_date": 1392036272` | +| Link | The value is a HTTP or HTTPS URI. | `"article": "https://example.org/ab1de.html"` | +| Rich Link | The value is a JSON object that contains `url` and `value` keys. | `"article": {"url": "https://example.org/ab1de.html", "value":"the dude abides"}` | +| Monetary Amount | The value is a JSON object that contains `amount` and `currency` keys. The `amount` key is a positive integer representing the amount in cents. The price in the example to the right denotes €349.99. | `"price": {"amount": 34999, "currency": "eur"}` | + +**Lead Events** + +When submitting events for Leads, you will need to specify the Lead's `id`. + +**Metadata behaviour** + +- We currently limit the number of tracked metadata keys to 10 per event. Once the quota is reached, we ignore any further keys we receive. The first 10 metadata keys are determined by the order in which they are sent in with the event. +- It is not possible to change the metadata keys once the event has been sent. A new event will need to be created with the new keys and you can archive the old one. +- There might be up to 24 hrs delay when you send a new metadata for an existing event. + +**Event de-duplication** + +The API may detect and ignore duplicate events. Each event is uniquely identified as a combination of the following data - the Workspace identifier, the Contact external identifier, the Data Event name and the Data Event created time. As a result, it is **strongly recommended** to send a second granularity Unix timestamp in the `created_at` field. + +Duplicated events are responded to using the normal `202 Accepted` code - an error is not thrown, however repeat requests will be counted against any rate limit that is in place. + +### HTTP API Responses + +- Successful responses to submitted events return `202 Accepted` with an empty body. +- Unauthorised access will be rejected with a `401 Unauthorized` or `403 Forbidden` response code. +- Events sent about users that cannot be found will return a `404 Not Found`. +- Event lists containing duplicate events will have those duplicates ignored. +- Server errors will return a `500` response code and may contain an error message in the body. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import CreateDataEventRequestWithId, Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.events.create( + request=CreateDataEventRequestWithId( + id="8a88a590-e1c3-41e2-a502-e0649dbf721c", + event_name="invited-friend", + created_at=1671028894, + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `CreateDataEventRequest` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.events.summaries(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Create event summaries for a user. Event summaries are used to track the number of times an event has occurred, the first time it occurred and the last time it occurred. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.events.summaries() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**user_id:** `typing.Optional[str]` — Your identifier for the user. + +
+
+ +
+
+ +**event_summaries:** `typing.Optional[CreateDataEventSummariesRequestEventSummaries]` — A list of event summaries for the user. Each event summary should contain the event name, the time the event occurred, and the number of times the event occurred. The event name should be a past tense 'verb-noun' combination, to improve readability, for example `updated-plan`. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Jobs +
client.jobs.status(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve the status of job execution. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.jobs.status( + job_id="job_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**job_id:** `str` — The unique identifier for the job which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Messages +
client.messages.create(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a message that has been initiated by an admin. The conversation can be either an in-app message or an email. + +> 🚧 Sending for visitors +> +> There can be a short delay between when a contact is created and when a contact becomes available to be messaged through the API. A 404 Not Found error will be returned in this case. + +This will return the Message model that has been created. + +> 🚧 Retrieving Associated Conversations +> +> As this is a message, there will be no conversation present until the contact responds. Once they do, you will have to search for a contact's conversations with the id of the message. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import ( + CreateMessageRequest_Email, + CreateMessageRequestFrom, + CreateMessageRequestTo, + Intercom, +) + +client = Intercom( + token="YOUR_TOKEN", +) +client.messages.create( + request=CreateMessageRequest_Email( + subject="Thanks for everything", + body="Hello there", + template="plain", + from_=CreateMessageRequestFrom( + id=394051, + ), + to=CreateMessageRequestTo( + type="user", + id="536e564f316c83104c000020", + ), + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `typing.Optional[CreateMessageRequest]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Segments +
client.segments.list(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all segments. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.segments.list( + include_count=True, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**include_count:** `typing.Optional[bool]` — It includes the count of contacts that belong to each segment. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.segments.find(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single segment. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.segments.find( + segment_id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**segment_id:** `str` — The unique identified of a given segment. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Subscription Types +
client.subscription_types.list() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can list all subscription types. A list of subscription type objects will be returned. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.subscription_types.list() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## PhoneCallRedirects +
client.phone_call_redirects.create(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can use the API to deflect phone calls to the Intercom Messenger. +Calling this endpoint will send an SMS with a link to the Messenger to the phone number specified. + +If custom attributes are specified, they will be added to the user or lead's custom data attributes. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import CreatePhoneSwitchRequest, Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.phone_call_redirects.create( + request=CreatePhoneSwitchRequest( + phone="+40241100100", + custom_attributes={"issue_type": "Billing", "priority": "High"}, + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `typing.Optional[CreatePhoneSwitchRequest]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Calls +
client.calls.list_calls(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve a paginated list of calls. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.calls.list_calls( + page=1, + per_page=1, +) + +``` +
+
+
+
+ +#### ⚙️ 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 25. Max 25. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.calls.show_call(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve a single call by id. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.calls.show_call( + call_id="call_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**call_id:** `str` — The id of the call to retrieve + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.calls.show_call_recording(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Redirects to a signed URL for the call's recording if it exists. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.calls.show_call_recording( + call_id="call_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**call_id:** `str` — The id of the call + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.calls.show_call_transcript(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Returns the transcript for the specified call as a downloadable text file. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.calls.show_call_transcript( + call_id="call_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**call_id:** `str` — The id of the call + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.calls.list_calls_with_transcripts(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve calls by a list of conversation ids and include transcripts when available. +A maximum of 20 `conversation_ids` can be provided. If none are provided or more than 20 are provided, a 400 error is returned. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.calls.list_calls_with_transcripts( + conversation_ids=["64619700005694", "64619700005695"], +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**conversation_ids:** `typing.Sequence[str]` — A list of conversation ids to fetch calls for. Maximum 20. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Teams +
client.teams.list() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +This will return a list of team objects for the App. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.teams.list() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.teams.find(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single team, containing an array of admins that belong to this team. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.teams.find( + team_id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**team_id:** `str` — The unique identifier of a given team. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Ticket States +
client.ticket_states.list_ticket_states() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can get a list of all ticket states for a workspace. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.ticket_states.list_ticket_states() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Ticket Types +
client.ticket_types.list() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can get a list of all ticket types for a workspace. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.ticket_types.list() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.ticket_types.create(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a new ticket type. +> 📘 Creating ticket types. +> +> Every ticket type will be created with two default attributes: _default_title_ and _default_description_. +> For the `icon` propery, use an emoji from [Twemoji Cheatsheet](https://twemoji-cheatsheet.vercel.app/) +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import CreateTicketTypeRequest, Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.ticket_types.create( + request=CreateTicketTypeRequest( + name="Customer Issue", + description="Customer Report Template", + category="Customer", + icon="🎟️", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `typing.Optional[CreateTicketTypeRequest]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.ticket_types.get(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single ticket type. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.ticket_types.get( + ticket_type_id="ticket_type_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**ticket_type_id:** `str` — The unique identifier for the ticket type which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.ticket_types.update(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ + +You can update a ticket type. + +> 📘 Updating a ticket type. +> +> For the `icon` propery, use an emoji from [Twemoji Cheatsheet](https://twemoji-cheatsheet.vercel.app/) +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.ticket_types.update( + ticket_type_id="ticket_type_id", + name="Bug Report 2", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**ticket_type_id:** `str` — The unique identifier for the ticket type which is given by Intercom. + +
+
+ +
+
+ +**name:** `typing.Optional[str]` — The name of the ticket type. + +
+
+ +
+
+ +**description:** `typing.Optional[str]` — The description of the ticket type. + +
+
+ +
+
+ +**category:** `typing.Optional[UpdateTicketTypeRequestCategory]` — Category of the Ticket Type. + +
+
+ +
+
+ +**icon:** `typing.Optional[str]` — The icon of the ticket type. + +
+
+ +
+
+ +**archived:** `typing.Optional[bool]` — The archived status of the ticket type. + +
+
+ +
+
+ +**is_internal:** `typing.Optional[bool]` — Whether the tickets associated with this ticket type are intended for internal use only or will be shared with customers. This is currently a limited attribute. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Tickets +
client.tickets.reply(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can reply to a ticket with a message from an admin or on behalf of a contact, or with a note for admins. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import ContactReplyTicketIntercomUserIdRequest, Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.tickets.reply( + ticket_id="123", + request=ContactReplyTicketIntercomUserIdRequest( + body="Thanks again :)", + intercom_user_id="6762f2971bb69f9f2193bc49", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**ticket_id:** `str` + +
+
+ +
+
+ +**request:** `TicketsReplyRequestBody` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.tickets.create(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a new ticket. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import CreateTicketRequestContactsItemId, Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.tickets.create( + ticket_type_id="1234", + contacts=[ + CreateTicketRequestContactsItemId( + id="6762f2d81bb69f9f2193bc54", + ) + ], +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**ticket_type_id:** `str` — The ID of the type of ticket you want to create + +
+
+ +
+
+ +**contacts:** `typing.Sequence[CreateTicketRequestContactsItem]` — The list of contacts (users or leads) affected by this ticket. Currently only one is allowed + +
+
+ +
+
+ +**skip_notifications:** `typing.Optional[bool]` — Option to disable notifications when a Ticket is created. + +
+
+ +
+
+ +**conversation_to_link_id:** `typing.Optional[str]` + +The ID of the conversation you want to link to the ticket. Here are the valid ways of linking two tickets: + - conversation | back-office ticket + - customer tickets | non-shared back-office ticket + - conversation | tracker ticket + - customer ticket | tracker ticket + +
+
+ +
+
+ +**company_id:** `typing.Optional[str]` — The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom + +
+
+ +
+
+ +**created_at:** `typing.Optional[int]` — The time the ticket was created. If not provided, the current time will be used. + +
+
+ +
+
+ +**assignment:** `typing.Optional[CreateTicketRequestAssignment]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.tickets.enqueue_create_ticket(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Enqueues ticket creation for asynchronous processing, returning if the job was enqueued successfully to be processed. We attempt to perform a best-effort validation on inputs before tasks are enqueued. If the given parameters are incorrect, we won't enqueue the job. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import CreateTicketRequestContactsItemId, Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.tickets.enqueue_create_ticket( + ticket_type_id="1234", + contacts=[ + CreateTicketRequestContactsItemId( + id="6762f2d81bb69f9f2193bc54", + ) + ], +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**ticket_type_id:** `str` — The ID of the type of ticket you want to create + +
+
+ +
+
+ +**contacts:** `typing.Sequence[CreateTicketRequestContactsItem]` — The list of contacts (users or leads) affected by this ticket. Currently only one is allowed + +
+
+ +
+
+ +**skip_notifications:** `typing.Optional[bool]` — Option to disable notifications when a Ticket is created. + +
+
+ +
+
+ +**conversation_to_link_id:** `typing.Optional[str]` + +The ID of the conversation you want to link to the ticket. Here are the valid ways of linking two tickets: + - conversation | back-office ticket + - customer tickets | non-shared back-office ticket + - conversation | tracker ticket + - customer ticket | tracker ticket + +
+
+ +
+
+ +**company_id:** `typing.Optional[str]` — The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom + +
+
+ +
+
+ +**created_at:** `typing.Optional[int]` — The time the ticket was created. If not provided, the current time will be used. + +
+
+ +
+
+ +**assignment:** `typing.Optional[CreateTicketRequestAssignment]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.tickets.get(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single ticket. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.tickets.get( + ticket_id="ticket_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**ticket_id:** `str` — The unique identifier for the ticket which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.tickets.update(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can update a ticket. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.tickets.update( + ticket_id="ticket_id", + ticket_state_id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**ticket_id:** `str` — The unique identifier for the ticket which is given by Intercom + +
+
+ +
+
+ +**ticket_attributes:** `typing.Optional[typing.Dict[str, typing.Any]]` — The attributes set on the ticket. + +
+
+ +
+
+ +**ticket_state_id:** `typing.Optional[str]` — The ID of the ticket state associated with the ticket type. + +
+
+ +
+
+ +**company_id:** `typing.Optional[str]` — The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom. Set to nil to remove company. + +
+
+ +
+
+ +**open:** `typing.Optional[bool]` — Specify if a ticket is open. Set to false to close a ticket. Closing a ticket will also unsnooze it. + +
+
+ +
+
+ +**is_shared:** `typing.Optional[bool]` — Specify whether the ticket is visible to users. + +
+
+ +
+
+ +**snoozed_until:** `typing.Optional[int]` — The time you want the ticket to reopen. + +
+
+ +
+
+ +**admin_id:** `typing.Optional[int]` — The ID of the admin performing ticket update. Needed for workflows execution and attributing actions to specific admins. + +
+
+ +
+
+ +**assignee_id:** `typing.Optional[str]` — The ID of the admin or team to which the ticket is assigned. Set this 0 to unassign it. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.tickets.delete_ticket(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can delete a ticket using the Intercom provided ID. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.tickets.delete_ticket( + ticket_id="ticket_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**ticket_id:** `str` — The unique identifier for the ticket which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.tickets.search(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can search for multiple tickets by the value of their attributes in order to fetch exactly which ones you want. + +To search for tickets, you send a `POST` request to `https://api.intercom.io/tickets/search`. + +This will accept a query object in the body which will define your filters. +{% 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 `20` 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 %} + +### 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 multiples 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 + +### Accepted Fields + +Most keys listed as part of the Ticket 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 `"foobar"`). +The `source.body` field is unique as the search will not be performed against the entire value, but instead against every element of the value separately. For example, when searching for a conversation with a `"I need support"` body - the query should contain a `=` operator with the value `"support"` for such conversation to be returned. A query with a `=` operator and a `"need support"` value will not yield a result. + +| Field | Type | +| :---------------------------------------- | :--------------------------------------------------------------------------------------- | +| id | String | +| created_at | Date (UNIX timestamp) | +| updated_at | Date (UNIX timestamp) | +| title | String | +| description | String | +| category | String | +| ticket_type_id | String | +| contact_ids | String | +| teammate_ids | String | +| admin_assignee_id | String | +| team_assignee_id | String | +| open | Boolean | +| state | String | +| snoozed_until | Date (UNIX timestamp) | +| ticket_attribute.{id} | String or Boolean or Date (UNIX timestamp) or Float or Integer | + +{% admonition type="info" name="Searching by Category" %} +When searching for tickets by the **`category`** field, specific terms must be used instead of the category names: +* For **Customer** category tickets, use the term `request`. +* For **Back-office** category tickets, use the term `task`. +* For **Tracker** category tickets, use the term `tracker`. +{% /admonition %} + +### Accepted Operators + +{% admonition type="info" name="Searching based on `created_at`" %} + You may 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 Shortcut for `OR` queries Values most be in Array | +| NIN | All | Not In Shortcut for `OR !` queries Values must be in Array | +| > | Integer Date (UNIX Timestamp) | Greater (or equal) than | +| < | Integer Date (UNIX Timestamp) | Lower (or equal) than | +| ~ | String | Contains | +| !~ | String | Doesn't Contain | +| ^ | String | Starts With | +| $ | String | Ends With | +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import ( + Intercom, + MultipleFilterSearchRequest, + SingleFilterSearchRequest, + StartingAfterPaging, +) + +client = Intercom( + token="YOUR_TOKEN", +) +response = client.tickets.search( + query=MultipleFilterSearchRequest( + operator="AND", + value=[ + SingleFilterSearchRequest( + field="created_at", + operator=">", + value="1306054154", + ) + ], + ), + pagination=StartingAfterPaging( + per_page=5, + ), +) +for item in response: + yield item +# alternatively, you can paginate page-by-page +for page in response.iter_pages(): + yield page + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**query:** `SearchRequestQuery` + +
+
+ +
+
+ +**pagination:** `typing.Optional[StartingAfterPaging]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Visitors +
client.visitors.find(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single visitor. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.visitors.find( + user_id="user_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**user_id:** `str` — The user_id of the Visitor you want to retrieve. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.visitors.update(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Sending a PUT request to `/visitors` will result in an update of an existing Visitor. + +**Option 1.** You can update a visitor by passing in the `user_id` of the visitor in the Request body. + +**Option 2.** You can update a visitor by passing in the `id` of the visitor in the Request body. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom, UpdateVisitorRequestWithUserId + +client = Intercom( + token="YOUR_TOKEN", +) +client.visitors.update( + request=UpdateVisitorRequestWithUserId( + user_id="fail", + name="Christian Fail", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `UpdateVisitorRequest` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.visitors.merge_to_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can merge a Visitor to a Contact of role type `lead` or `user`. + +> 📘 What happens upon a visitor being converted? +> +> If the User exists, then the Visitor will be merged into it, the Visitor deleted and the User returned. If the User does not exist, the Visitor will be converted to a User, with the User identifiers replacing it's Visitor identifiers. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.visitors import UserWithId, VisitorWithUserId + +client = Intercom( + token="YOUR_TOKEN", +) +client.visitors.merge_to_contact( + type="user", + user=UserWithId( + id="8a88a590-e1c3-41e2-a502-e0649dbf721c", + email="foo@bar.com", + ), + visitor=VisitorWithUserId( + user_id="3ecf64d0-9ed1-4e9f-88e1-da7d6e6782f3", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**type:** `str` — Represents the role of the Contact model. Accepts `lead` or `user`. + +
+
+ +
+
+ +**user:** `ConvertVisitorRequestUser` — The unique identifiers retained after converting or merging. + +
+
+ +
+
+ +**visitor:** `ConvertVisitorRequestVisitor` — The unique identifiers to convert a single Visitor. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## HelpCenters Collections +
client.help_centers.collections.list(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all collections by making a GET request to `https://api.intercom.io/help_center/collections`. + +Collections will be returned in descending order on the `updated_at` attribute. This means if you need to iterate through results then we'll show the most recently updated collections first. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +response = client.help_centers.collections.list() +for item in response: + yield item +# alternatively, you can paginate page-by-page +for page in response.iter_pages(): + yield page + +``` +
+
+
+
+ +#### ⚙️ 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 + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.help_centers.collections.create(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a new collection by making a POST request to `https://api.intercom.io/help_center/collections.` +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.help_centers.collections.create( + name="collection 51", + description="Missing required parameter", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**name:** `str` — The name of the collection. For multilingual collections, this will be the name of the default language's content. + +
+
+ +
+
+ +**description:** `typing.Optional[str]` — The description of the collection. For multilingual collections, this will be the description of the default language's content. + +
+
+ +
+
+ +**translated_content:** `typing.Optional[GroupTranslatedContent]` + +
+
+ +
+
+ +**parent_id:** `typing.Optional[str]` — The id of the parent collection. If `null` then it will be created as the first level collection. + +
+
+ +
+
+ +**help_center_id:** `typing.Optional[int]` — The id of the help center where the collection will be created. If `null` then it will be created in the default help center. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.help_centers.collections.find(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single collection by making a GET request to `https://api.intercom.io/help_center/collections/`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.help_centers.collections.find( + collection_id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**collection_id:** `int` — The unique identifier for the collection which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.help_centers.collections.update(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can update the details of a single collection by making a PUT request to `https://api.intercom.io/collections/`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.help_centers.collections.update( + collection_id=1, + name="Update collection name", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**collection_id:** `int` — The unique identifier for the collection which is given by Intercom. + +
+
+ +
+
+ +**name:** `typing.Optional[str]` — The name of the collection. For multilingual collections, this will be the name of the default language's content. + +
+
+ +
+
+ +**description:** `typing.Optional[str]` — The description of the collection. For multilingual collections, this will be the description of the default language's content. + +
+
+ +
+
+ +**translated_content:** `typing.Optional[GroupTranslatedContent]` + +
+
+ +
+
+ +**parent_id:** `typing.Optional[str]` — The id of the parent collection. If `null` then it will be updated as the first level collection. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.help_centers.collections.delete(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can delete a single collection by making a DELETE request to `https://api.intercom.io/collections/`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.help_centers.collections.delete( + collection_id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**collection_id:** `int` — The unique identifier for the collection which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## News Items +
client.news.items.list() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all news items +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.news.items.list() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.news.items.create(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a news item +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.news import NewsfeedAssignment + +client = Intercom( + token="YOUR_TOKEN", +) +client.news.items.create( + title="Halloween is here!", + body="

New costumes in store for this spooky season

", + sender_id=991267834, + state="live", + deliver_silently=True, + labels=["Product", "Update", "New"], + reactions=["😆", "😅"], + newsfeed_assignments=[ + NewsfeedAssignment( + newsfeed_id=53, + published_at=1664638214, + ) + ], +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**title:** `str` — The title of the news item. + +
+
+ +
+
+ +**sender_id:** `int` — The id of the sender of the news item. Must be a teammate on the workspace. + +
+
+ +
+
+ +**body:** `typing.Optional[str]` — The news item body, which may contain HTML. + +
+
+ +
+
+ +**state:** `typing.Optional[NewsItemRequestState]` — News items will not be visible to your users in the assigned newsfeeds until they are set live. + +
+
+ +
+
+ +**deliver_silently:** `typing.Optional[bool]` — When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + +
+
+ +
+
+ +**labels:** `typing.Optional[typing.Sequence[str]]` — Label names displayed to users to categorize the news item. + +
+
+ +
+
+ +**reactions:** `typing.Optional[typing.Sequence[typing.Optional[str]]]` — Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + +
+
+ +
+
+ +**newsfeed_assignments:** `typing.Optional[typing.Sequence[NewsfeedAssignment]]` — A list of newsfeed_assignments to assign to the specified newsfeed. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.news.items.find(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single news item. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.news.items.find( + news_item_id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**news_item_id:** `int` — The unique identifier for the news item which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.news.items.update(...) +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.news.items.update( + news_item_id=1, + title="Christmas is here!", + body="

New gifts in store for the jolly season

", + sender_id=991267848, + reactions=["😝", "😂"], +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**news_item_id:** `int` — The unique identifier for the news item which is given by Intercom. + +
+
+ +
+
+ +**title:** `str` — The title of the news item. + +
+
+ +
+
+ +**sender_id:** `int` — The id of the sender of the news item. Must be a teammate on the workspace. + +
+
+ +
+
+ +**body:** `typing.Optional[str]` — The news item body, which may contain HTML. + +
+
+ +
+
+ +**state:** `typing.Optional[NewsItemRequestState]` — News items will not be visible to your users in the assigned newsfeeds until they are set live. + +
+
+ +
+
+ +**deliver_silently:** `typing.Optional[bool]` — When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + +
+
+ +
+
+ +**labels:** `typing.Optional[typing.Sequence[str]]` — Label names displayed to users to categorize the news item. + +
+
+ +
+
+ +**reactions:** `typing.Optional[typing.Sequence[typing.Optional[str]]]` — Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + +
+
+ +
+
+ +**newsfeed_assignments:** `typing.Optional[typing.Sequence[NewsfeedAssignment]]` — A list of newsfeed_assignments to assign to the specified newsfeed. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.news.items.delete(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can delete a single news item. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.news.items.delete( + news_item_id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**news_item_id:** `int` — The unique identifier for the news item which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## News Feeds +
client.news.feeds.list_items(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all news items that are live on a given newsfeed +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.news.feeds.list_items( + newsfeed_id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**newsfeed_id:** `str` — The unique identifier for the news feed item which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.news.feeds.list() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all newsfeeds +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.news.feeds.list() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.news.feeds.find(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single newsfeed +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.news.feeds.find( + newsfeed_id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**newsfeed_id:** `str` — The unique identifier for the news feed item which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## TicketTypes Attributes +
client.ticket_types.attributes.create(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a new attribute for a ticket type. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.ticket_types.attributes.create( + ticket_type_id="ticket_type_id", + name="Attribute Title", + description="Attribute Description", + data_type="string", + required_to_create=False, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**ticket_type_id:** `str` — The unique identifier for the ticket type which is given by Intercom. + +
+
+ +
+
+ +**name:** `str` — The name of the ticket type attribute + +
+
+ +
+
+ +**description:** `str` — The description of the attribute presented to the teammate or contact + +
+
+ +
+
+ +**data_type:** `CreateTicketTypeAttributeRequestDataType` — The data type of the attribute + +
+
+ +
+
+ +**required_to_create:** `typing.Optional[bool]` — Whether the attribute is required to be filled in when teammates are creating the ticket in Inbox. + +
+
+ +
+
+ +**required_to_create_for_contacts:** `typing.Optional[bool]` — Whether the attribute is required to be filled in when contacts are creating the ticket in Messenger. + +
+
+ +
+
+ +**visible_on_create:** `typing.Optional[bool]` — Whether the attribute is visible to teammates when creating a ticket in Inbox. + +
+
+ +
+
+ +**visible_to_contacts:** `typing.Optional[bool]` — Whether the attribute is visible to contacts when creating a ticket in Messenger. + +
+
+ +
+
+ +**multiline:** `typing.Optional[bool]` — Whether the attribute allows multiple lines of text (only applicable to string attributes) + +
+
+ +
+
+ +**list_items:** `typing.Optional[str]` — A comma delimited list of items for the attribute value (only applicable to list attributes) + +
+
+ +
+
+ +**allow_multiple_values:** `typing.Optional[bool]` — Whether the attribute allows multiple files to be attached to it (only applicable to file attributes) + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.ticket_types.attributes.update(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can update an existing attribute for a ticket type. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.ticket_types.attributes.update( + ticket_type_id="ticket_type_id", + attribute_id="attribute_id", + description="New Attribute Description", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**ticket_type_id:** `str` — The unique identifier for the ticket type which is given by Intercom. + +
+
+ +
+
+ +**attribute_id:** `str` — The unique identifier for the ticket type attribute which is given by Intercom. + +
+
+ +
+
+ +**name:** `typing.Optional[str]` — The name of the ticket type attribute + +
+
+ +
+
+ +**description:** `typing.Optional[str]` — The description of the attribute presented to the teammate or contact + +
+
+ +
+
+ +**required_to_create:** `typing.Optional[bool]` — Whether the attribute is required to be filled in when teammates are creating the ticket in Inbox. + +
+
+ +
+
+ +**required_to_create_for_contacts:** `typing.Optional[bool]` — Whether the attribute is required to be filled in when contacts are creating the ticket in Messenger. + +
+
+ +
+
+ +**visible_on_create:** `typing.Optional[bool]` — Whether the attribute is visible to teammates when creating a ticket in Inbox. + +
+
+ +
+
+ +**visible_to_contacts:** `typing.Optional[bool]` — Whether the attribute is visible to contacts when creating a ticket in Messenger. + +
+
+ +
+
+ +**multiline:** `typing.Optional[bool]` — Whether the attribute allows multiple lines of text (only applicable to string attributes) + +
+
+ +
+
+ +**list_items:** `typing.Optional[str]` — A comma delimited list of items for the attribute value (only applicable to list attributes) + +
+
+ +
+
+ +**allow_multiple_values:** `typing.Optional[bool]` — Whether the attribute allows multiple files to be attached to it (only applicable to file attributes) + +
+
+ +
+
+ +**archived:** `typing.Optional[bool]` — Whether the attribute should be archived and not shown during creation of the ticket (it will still be present on previously created tickets) + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Admins +
client.unstable.admins.identify_admin() +
+
+ +#### 📝 Description + +
+
+ +
+
+ + +You can view the currently authorised admin along with the embedded app object (a "workspace" in legacy terminology). + +> 🚧 Single Sign On +> +> If you are building a custom "Log in with Intercom" flow for your site, and you call the `/me` endpoint to identify the logged-in user, you should not accept any sign-ins from users with unverified email addresses as it poses a potential impersonation security risk. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.admins.identify_admin() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.admins.set_away_admin(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can set an Admin as away for the Inbox. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.admins.set_away_admin( + id=1, + away_mode_enabled=True, + away_mode_reassign=True, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The unique identifier of a given admin + +
+
+ +
+
+ +**away_mode_enabled:** `bool` — Set to "true" to change the status of the admin to away. + +
+
+ +
+
+ +**away_mode_reassign:** `bool` — Set to "true" to assign any new conversation replies to your default inbox. + +
+
+ +
+
+ +**away_status_reason_id:** `typing.Optional[int]` — The unique identifier of the away status reason + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.admins.list_activity_logs(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can get a log of activities by all admins in an app. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.admins.list_activity_logs( + created_at_after="1677253093", + created_at_before="1677861493", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**created_at_after:** `str` — The start date that you request data for. It must be formatted as a UNIX timestamp. + +
+
+ +
+
+ +**created_at_before:** `typing.Optional[str]` — The end date that you request data for. It must be formatted as a UNIX timestamp. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.admins.list_admins() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of admins for a given workspace. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.admins.list_admins() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.admins.retrieve_admin(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can retrieve the details of a single admin. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.admins.retrieve_admin( + id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The unique identifier of a given admin + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## AI Content +
client.unstable.ai_content.list_content_import_sources() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can retrieve a list of all content import sources for a workspace. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.ai_content.list_content_import_sources() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.ai_content.create_content_import_source(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a new content import source by sending a POST request to this endpoint. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.ai_content.create_content_import_source( + url="https://www.example.com", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**url:** `str` — The URL of the content import source. + +
+
+ +
+
+ +**status:** `typing.Optional[CreateContentImportSourceRequestStatus]` — The status of the content import source. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.ai_content.get_content_import_source(...) +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.ai_content.get_content_import_source( + id="id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the content import source which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.ai_content.update_content_import_source(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can update an existing content import source. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.ai_content.update_content_import_source( + id="id", + sync_behavior="api", + url="https://www.example.com", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the content import source which is given by Intercom. + +
+
+ +
+
+ +**sync_behavior:** `UpdateContentImportSourceRequestSyncBehavior` — If you intend to create or update External Pages via the API, this should be set to `api`. You can not change the value to or from api. + +
+
+ +
+
+ +**url:** `str` — The URL of the content import source. This may only be different from the existing value if the sync behavior is API. + +
+
+ +
+
+ +**status:** `typing.Optional[UpdateContentImportSourceRequestStatus]` — The status of the content import source. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.ai_content.delete_content_import_source(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can delete a content import source by making a DELETE request this endpoint. This will also delete all external pages that were imported from this source. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.ai_content.delete_content_import_source( + id="id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the content import source which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.ai_content.list_external_pages() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can retrieve a list of all external pages for a workspace. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.ai_content.list_external_pages() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.ai_content.create_external_page(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a new external page by sending a POST request to this endpoint. If an external page already exists with the specified source_id and external_id, it will be updated instead. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.ai_content.create_external_page( + title="Test", + html="

Test

", + url="https://www.example.com", + source_id=44, + external_id="abc1234", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**title:** `str` — The title of the external page. + +
+
+ +
+
+ +**html:** `str` — The body of the external page in HTML. + +
+
+ +
+
+ +**source_id:** `int` — The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + +
+
+ +
+
+ +**external_id:** `str` — The identifier for the external page which was given by the source. Must be unique for the source. + +
+
+ +
+
+ +**url:** `typing.Optional[str]` — The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. When a URL is not present, Fin will not reference the source. + +
+
+ +
+
+ +**ai_agent_availability:** `typing.Optional[bool]` — Whether the external page should be used to answer questions by AI Agent. Will not default when updating an existing external page. + +
+
+ +
+
+ +**ai_copilot_availability:** `typing.Optional[bool]` — Whether the external page should be used to answer questions by AI Copilot. Will not default when updating an existing external page. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.ai_content.get_external_page(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can retrieve an external page. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.ai_content.get_external_page( + id="id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the external page which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.ai_content.update_external_page(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can update an existing external page (if it was created via the API). +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.ai_content.update_external_page( + id="id", + title="Test", + html="

Test

", + url="https://www.example.com", + source_id=47, + external_id="5678", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the external page which is given by Intercom. + +
+
+ +
+
+ +**title:** `str` — The title of the external page. + +
+
+ +
+
+ +**html:** `str` — The body of the external page in HTML. + +
+
+ +
+
+ +**url:** `str` — The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. + +
+
+ +
+
+ +**source_id:** `int` — The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + +
+
+ +
+
+ +**fin_availability:** `typing.Optional[bool]` — Whether the external page should be used to answer questions by Fin. + +
+
+ +
+
+ +**external_id:** `typing.Optional[str]` — The identifier for the external page which was given by the source. Must be unique for the source. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.ai_content.delete_external_page(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Sending a DELETE request for an external page will remove it from the content library UI and from being used for AI answers. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.ai_content.delete_external_page( + id="id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the external page which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Articles +
client.unstable.articles.list_articles() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all articles by making a GET request to `https://api.intercom.io/articles`. + +> 📘 How are the articles sorted and ordered? +> +> Articles will be returned in descending order on the `updated_at` attribute. This means if you need to iterate through results then we'll show the most recently updated articles first. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.articles.list_articles() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.articles.create_article(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a new article by making a POST request to `https://api.intercom.io/articles`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.articles.create_article( + request={"key": "value"}, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `typing.Any` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.articles.retrieve_article(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single article by making a GET request to `https://api.intercom.io/articles/`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.articles.retrieve_article( + id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The unique identifier for the article which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.articles.delete_article(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can delete a single article by making a DELETE request to `https://api.intercom.io/articles/`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.articles.delete_article( + id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The unique identifier for the article which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.articles.search_articles(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can search for articles by making a GET request to `https://api.intercom.io/articles/search`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.articles.search_articles( + phrase="Getting started", + state="published", + help_center_id=1, + highlight=True, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**phrase:** `typing.Optional[str]` — The phrase within your articles to search for. + +
+
+ +
+
+ +**state:** `typing.Optional[str]` — The state of the Articles returned. One of `published`, `draft` or `all`. + +
+
+ +
+
+ +**help_center_id:** `typing.Optional[int]` — The ID of the Help Center to search in. + +
+
+ +
+
+ +**highlight:** `typing.Optional[bool]` — Return a highlighted version of the matching content within your articles. Refer to the response schema for more details. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Away Status Reasons +
client.unstable.away_status_reasons.list_away_status_reasons() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Returns a list of all away status reasons configured for the workspace, including deleted ones. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.away_status_reasons.list_away_status_reasons() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Unstable Export +
client.unstable.export.enqueue_a_new_reporting_data_export_job(...) +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.export.enqueue_a_new_reporting_data_export_job( + dataset_id="conversation", + attribute_ids=["conversation_id", "conversation_started_at"], + start_time=1717490000, + end_time=1717510000, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**dataset_id:** `str` + +
+
+ +
+
+ +**attribute_ids:** `typing.Sequence[str]` + +
+
+ +
+
+ +**start_time:** `int` + +
+
+ +
+
+ +**end_time:** `int` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.export.list_available_datasets_and_attributes() +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.export.list_available_datasets_and_attributes() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Help Center +
client.unstable.help_center.list_all_collections() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all collections by making a GET request to `https://api.intercom.io/help_center/collections`. + +Collections will be returned in descending order on the `updated_at` attribute. This means if you need to iterate through results then we'll show the most recently updated collections first. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.help_center.list_all_collections() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.help_center.create_collection(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a new collection by making a POST request to `https://api.intercom.io/help_center/collections.` +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.help_center.create_collection( + name="collection 51", + description="Missing required parameter", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**name:** `str` — The name of the collection. For multilingual collections, this will be the name of the default language's content. + +
+
+ +
+
+ +**description:** `typing.Optional[str]` — The description of the collection. For multilingual collections, this will be the description of the default language's content. + +
+
+ +
+
+ +**translated_content:** `typing.Optional[GroupTranslatedContent]` + +
+
+ +
+
+ +**parent_id:** `typing.Optional[str]` — The id of the parent collection. If `null` then it will be created as the first level collection. + +
+
+ +
+
+ +**help_center_id:** `typing.Optional[int]` — The id of the help center where the collection will be created. If `null` then it will be created in the default help center. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.help_center.retrieve_collection(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single collection by making a GET request to `https://api.intercom.io/help_center/collections/`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.help_center.retrieve_collection( + id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The unique identifier for the collection which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.help_center.update_collection(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can update the details of a single collection by making a PUT request to `https://api.intercom.io/collections/`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.help_center.update_collection( + id=1, + name="Update collection name", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The unique identifier for the collection which is given by Intercom. + +
+
+ +
+
+ +**name:** `typing.Optional[str]` — The name of the collection. For multilingual collections, this will be the name of the default language's content. + +
+
+ +
+
+ +**description:** `typing.Optional[str]` — The description of the collection. For multilingual collections, this will be the description of the default language's content. + +
+
+ +
+
+ +**translated_content:** `typing.Optional[GroupTranslatedContent]` + +
+
+ +
+
+ +**parent_id:** `typing.Optional[str]` — The id of the parent collection. If `null` then it will be updated as the first level collection. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.help_center.delete_collection(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can delete a single collection by making a DELETE request to `https://api.intercom.io/collections/`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.help_center.delete_collection( + id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The unique identifier for the collection which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.help_center.retrieve_help_center(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single Help Center by making a GET request to `https://api.intercom.io/help_center/help_center/`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.help_center.retrieve_help_center( + id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The unique identifier for the collection which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.help_center.list_help_centers() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can list all Help Centers by making a GET request to `https://api.intercom.io/help_center/help_centers`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.help_center.list_help_centers() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Internal Articles +
client.unstable.internal_articles.list_internal_articles() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all internal articles by making a GET request to `https://api.intercom.io/internal_articles`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.internal_articles.list_internal_articles() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.internal_articles.create_internal_article(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a new internal article by making a POST request to `https://api.intercom.io/internal_articles`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.unstable import CreateInternalArticleRequest + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.internal_articles.create_internal_article( + request=CreateInternalArticleRequest( + title="Thanks for everything", + body="Body of the Internal Article", + author_id=1295, + owner_id=1295, + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `typing.Optional[CreateInternalArticleRequest]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.internal_articles.retrieve_internal_article(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single internal article by making a GET request to `https://api.intercom.io/internal_articles/`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.internal_articles.retrieve_internal_article( + id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The unique identifier for the article which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.internal_articles.update_internal_article(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can update the details of a single internal article by making a PUT request to `https://api.intercom.io/internal_articles/`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.internal_articles.update_internal_article( + id=1, + title="Christmas is here!", + body="

New gifts in store for the jolly season

", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The unique identifier for the internal article which is given by Intercom. + +
+
+ +
+
+ +**title:** `typing.Optional[str]` — The title of the article. + +
+
+ +
+
+ +**body:** `typing.Optional[str]` — The content of the article. + +
+
+ +
+
+ +**author_id:** `typing.Optional[int]` — The id of the author of the article. + +
+
+ +
+
+ +**owner_id:** `typing.Optional[int]` — The id of the author of the article. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.internal_articles.delete_internal_article(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can delete a single internal article by making a DELETE request to `https://api.intercom.io/internal_articles/`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.internal_articles.delete_internal_article( + id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The unique identifier for the internal article which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.internal_articles.search_internal_articles(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can search for internal articles by making a GET request to `https://api.intercom.io/internal_articles/search`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.internal_articles.search_internal_articles( + folder_id="folder_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**folder_id:** `typing.Optional[str]` — The ID of the folder to search in. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Companies +
client.unstable.companies.retrieve_company(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a single company by passing in `company_id` or `name`. + + `https://api.intercom.io/companies?name={name}` + + `https://api.intercom.io/companies?company_id={company_id}` + +You can fetch all companies and filter by `segment_id` or `tag_id` as a query parameter. + + `https://api.intercom.io/companies?tag_id={tag_id}` + + `https://api.intercom.io/companies?segment_id={segment_id}` +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.companies.retrieve_company( + name="my company", + company_id="12345", + tag_id="678910", + segment_id="98765", + page=1, + per_page=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**name:** `typing.Optional[str]` — The `name` of the company to filter by. + +
+
+ +
+
+ +**company_id:** `typing.Optional[str]` — The `company_id` of the company to filter by. + +
+
+ +
+
+ +**tag_id:** `typing.Optional[str]` — The `tag_id` of the company to filter by. + +
+
+ +
+
+ +**segment_id:** `typing.Optional[str]` — The `segment_id` of the company to filter by. + +
+
+ +
+
+ +**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. + +
+
+
+
+ + +
+
+
+ +
client.unstable.companies.create_or_update_company(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create or update a company. + +Companies will be only visible in Intercom when there is at least one associated user. + +Companies are looked up via `company_id` in a `POST` request, if not found via `company_id`, the new company will be created, if found, that company will be updated. + +{% admonition type="warning" name="Using `company_id`" %} + You can set a unique `company_id` value when creating a company. However, it is not possible to update `company_id`. Be sure to set a unique value once upon creation of the company. +{% /admonition %} +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.companies.create_or_update_company( + request={"key": "value"}, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `typing.Any` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.companies.retrieve_a_company_by_id(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a single company. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.companies.retrieve_a_company_by_id( + id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the company which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.companies.update_company(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can update a single company using the Intercom provisioned `id`. + +{% admonition type="warning" name="Using `company_id`" %} + When updating a company it is not possible to update `company_id`. This can only be set once upon creation of the company. +{% /admonition %} +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.companies.update_company( + id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the company which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.companies.delete_company(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can delete a single company. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.companies.delete_company( + id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the company which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.companies.list_attached_contacts(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all contacts that belong to a company. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.companies.list_attached_contacts( + id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the company which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.companies.list_attached_segments_for_companies(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all segments that belong to a company. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.companies.list_attached_segments_for_companies( + id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the company which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.companies.list_all_companies(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can list companies. The company list is sorted by the `last_request_at` field and by default is ordered descending, most recently requested first. + +Note that the API does not include companies who have no associated users in list responses. + +When using the Companies endpoint and the pages object to iterate through the returned companies, there is a limit of 10,000 Companies that can be returned. If you need to list or iterate on more than 10,000 Companies, please use the [Scroll API](https://developers.intercom.com/reference#iterating-over-all-companies). +{% admonition type="warning" name="Pagination" %} + You can use pagination to limit the number of results returned. The default is `20` 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 %} +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.companies.list_all_companies( + page=1, + per_page=1, + order="desc", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**page:** `typing.Optional[int]` — The page of results to fetch. Defaults to first page + +
+
+ +
+
+ +**per_page:** `typing.Optional[int]` — How many results to return per page. Defaults to 15 + +
+
+ +
+
+ +**order:** `typing.Optional[str]` — `asc` or `desc`. Return the companies in ascending or descending order. Defaults to desc + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.companies.scroll_over_all_companies(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ + The `list all companies` functionality does not work well for huge datasets, and can result in errors and performance problems when paging deeply. The Scroll API provides an efficient mechanism for iterating over all companies in a dataset. + +- Each app can only have 1 scroll open at a time. You'll get an error message if you try to have more than one open per app. +- If the scroll isn't used for 1 minute, it expires and calls with that scroll param will fail +- If the end of the scroll is reached, "companies" will be empty and the scroll parameter will expire + +{% admonition type="info" name="Scroll Parameter" %} + You can get the first page of companies by simply sending a GET request to the scroll endpoint. + For subsequent requests you will need to use the scroll parameter from the response. +{% /admonition %} +{% admonition type="danger" name="Scroll network timeouts" %} + Since scroll is often used on large datasets network errors such as timeouts can be encountered. When this occurs you will see a HTTP 500 error with the following message: + "Request failed due to an internal network error. Please restart the scroll operation." + If this happens, you will need to restart your scroll query: It is not possible to continue from a specific point when using scroll. +{% /admonition %} +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.companies.scroll_over_all_companies( + scroll_param="scroll_param", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**scroll_param:** `typing.Optional[str]` — + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.companies.attach_contact_to_a_company(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can attach a company to a single contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.companies.attach_contact_to_a_company( + id="id", + company_id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the contact which is given by Intercom + +
+
+ +
+
+ +**company_id:** `str` — The unique identifier for the company which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.companies.detach_contact_from_a_company(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can detach a company from a single contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.companies.detach_contact_from_a_company( + contact_id="58a430d35458202d41b1e65b", + id="58a430d35458202d41b1e65b", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — The unique identifier for the contact which is given by Intercom + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the company which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Notes +
client.unstable.notes.list_company_notes(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of notes that are associated to a company. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.notes.list_company_notes( + id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the company which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.notes.list_notes(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of notes that are associated to a contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.notes.list_notes( + id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The unique identifier of a contact. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.notes.create_note(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can add a note to a single contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.notes.create_note( + id=1, + body="Hello", + contact_id="123", + admin_id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The unique identifier of a given contact. + +
+
+ +
+
+ +**body:** `str` — The text of the note. + +
+
+ +
+
+ +**contact_id:** `typing.Optional[str]` — The unique identifier of a given contact. + +
+
+ +
+
+ +**admin_id:** `typing.Optional[str]` — The unique identifier of a given admin. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.notes.retrieve_note(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single note. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.notes.retrieve_note( + id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The unique identifier of a given note + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Contacts +
client.unstable.contacts.list_companies_for_a_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of companies that are associated to a contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.contacts.list_companies_for_a_contact( + id="63a07ddf05a32042dffac965", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the contact which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.contacts.list_segments_for_a_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of segments that are associated to a contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.contacts.list_segments_for_a_contact( + contact_id="63a07ddf05a32042dffac965", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — The unique identifier for the contact which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.contacts.list_subscriptions_for_a_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.contacts.list_subscriptions_for_a_contact( + contact_id="63a07ddf05a32042dffac965", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — The unique identifier for the contact which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.contacts.list_tags_for_a_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all tags that are attached to a specific contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.contacts.list_tags_for_a_contact( + contact_id="63a07ddf05a32042dffac965", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — The unique identifier for the contact which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.contacts.show_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.contacts.show_contact( + id="63a07ddf05a32042dffac965", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — id + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.contacts.update_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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 %} +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.contacts.update_contact( + id="63a07ddf05a32042dffac965", + custom_attributes={"order": ["21"]}, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**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. + +
+
+
+
+ + +
+
+
+ +
client.unstable.contacts.delete_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can delete a single contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.contacts.delete_contact( + id="id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — id + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.contacts.merge_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can merge a contact with a `role` of `lead` into a contact with a `role` of `user`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.contacts.merge_contact( + from_="6762f0d51bb69f9f2193bb7f", + into="6762f0d51bb69f9f2193bb80", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**from_:** `typing.Optional[str]` — The unique identifier for the contact to merge away from. Must be a lead. + +
+
+ +
+
+ +**into:** `typing.Optional[str]` — The unique identifier for the contact to merge into. Must be a user. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.contacts.search_contacts(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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
Accepts user or lead | +| name | String | +| avatar | String | +| owner_id | Integer | +| email | String | +| email_domain | String | +| phone | String | +| formatted_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
Shortcut for `OR` queries
Values must be in Array | +| NIN | All | Not In
Shortcut for `OR !` queries
Values must be in Array | +| > | Integer
Date (UNIX Timestamp) | Greater than | +| < | Integer
Date (UNIX Timestamp) | Lower than | +| ~ | String | Contains | +| !~ | String | Doesn't Contain | +| ^ | String | Starts With | +| $ | String | Ends With | +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.unstable import ( + MultipleFilterSearchRequest, + SingleFilterSearchRequest, + StartingAfterPaging, +) + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.contacts.search_contacts( + query=MultipleFilterSearchRequest( + operator="AND", + value=[ + SingleFilterSearchRequest( + field="created_at", + operator=">", + value="1306054154", + ) + ], + ), + pagination=StartingAfterPaging( + per_page=5, + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**query:** `SearchRequestQuery` + +
+
+ +
+
+ +**pagination:** `typing.Optional[StartingAfterPaging]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.contacts.list_contacts() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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 %} +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.contacts.list_contacts() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.contacts.create_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a new contact (ie. user or lead). +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.contacts.create_contact( + request={"email": "joebloggs@intercom.io"}, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `CreateContactRequestTwo` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.contacts.show_contact_by_external_id(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single contact by external ID. Note that this endpoint only supports users and not leads. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.contacts.show_contact_by_external_id( + external_id="cdd29344-5e0c-4ef0-ac56-f9ba2979bc27", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**external_id:** `str` — The external ID of the user that you want to retrieve + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.contacts.archive_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can archive a single contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.contacts.archive_contact( + id="63a07ddf05a32042dffac965", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — id + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.contacts.unarchive_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can unarchive a single contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.contacts.unarchive_contact( + id="63a07ddf05a32042dffac965", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — id + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.contacts.block_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Block a single contact.
**Note:** conversations of the contact will also be archived during the process.
More details in [FAQ How do I block Inbox spam?](https://www.intercom.com/help/en/articles/8838656-inbox-faqs) +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.contacts.block_contact( + id="63a07ddf05a32042dffac965", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — id + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Subscription Types +
client.unstable.subscription_types.attach_subscription_type_to_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.subscription_types.attach_subscription_type_to_contact( + contact_id="63a07ddf05a32042dffac965", + id="invalid_id", + consent_type="opt_in", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — The unique identifier for the contact which is given by Intercom + +
+
+ +
+
+ +**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. + +
+
+
+
+ + +
+
+
+ +
client.unstable.subscription_types.detach_subscription_type_to_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +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. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.subscription_types.detach_subscription_type_to_contact( + contact_id="63a07ddf05a32042dffac965", + id="37846", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — The unique identifier for the contact which is given by Intercom + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the subscription type which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.subscription_types.list_subscription_types() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can list all subscription types. A list of subscription type objects will be returned. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.subscription_types.list_subscription_types() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Tags +
client.unstable.tags.attach_tag_to_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can tag a specific contact. This will return a tag object for the tag that was added to the contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.tags.attach_tag_to_contact( + contact_id="63a07ddf05a32042dffac965", + id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — The unique identifier for the contact which is given by Intercom + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the tag which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.tags.detach_tag_from_contact(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can remove tag from a specific contact. This will return a tag object for the tag that was removed from the contact. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.tags.detach_tag_from_contact( + contact_id="63a07ddf05a32042dffac965", + id="7522907", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**contact_id:** `str` — The unique identifier for the contact which is given by Intercom + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the tag which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.tags.attach_tag_to_conversation(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can tag a specific conversation. This will return a tag object for the tag that was added to the conversation. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.tags.attach_tag_to_conversation( + conversation_id="64619700005694", + id="7522907", + admin_id="780", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**conversation_id:** `str` — conversation_id + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the tag which is given by Intercom + +
+
+ +
+
+ +**admin_id:** `str` — The unique identifier for the admin which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.tags.detach_tag_from_conversation(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can remove tag from a specific conversation. This will return a tag object for the tag that was removed from the conversation. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.tags.detach_tag_from_conversation( + conversation_id="64619700005694", + id="7522907", + admin_id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**conversation_id:** `str` — conversation_id + +
+
+ +
+
+ +**id:** `str` — id + +
+
+ +
+
+ +**admin_id:** `str` — The unique identifier for the admin which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.tags.list_tags() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all tags for a given workspace. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.tags.list_tags() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.tags.create_tag(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can use this endpoint to perform the following operations: + + **1. Create a new tag:** You can create a new tag by passing in the tag name as specified in "Create or Update Tag Request Payload" described below. + + **2. Update an existing tag:** You can update an existing tag by passing the id of the tag as specified in "Create or Update Tag Request Payload" described below. + + **3. Tag Companies:** You can tag single company or a list of companies. You can tag a company by passing in the tag name and the company details as specified in "Tag Company Request Payload" described below. Also, if the tag doesn't exist then a new one will be created automatically. + + **4. Untag Companies:** You can untag a single company or a list of companies. You can untag a company by passing in the tag id and the company details as specified in "Untag Company Request Payload" described below. + + **5. Tag Multiple Users:** You can tag a list of users. You can tag the users by passing in the tag name and the user details as specified in "Tag Users Request Payload" described below. + +Each operation will return a tag object. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.unstable import ( + TagMultipleUsersRequest, + TagMultipleUsersRequestUsersItem, +) + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.tags.create_tag( + request=TagMultipleUsersRequest( + name="test", + users=[ + TagMultipleUsersRequestUsersItem( + id="123", + ) + ], + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `CreateTagRequestBody` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.tags.find_tag(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of tags that are on the workspace by their id. +This will return a tag object. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.tags.find_tag( + id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier of a given tag + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.tags.delete_tag(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can delete the details of tags that are on the workspace by passing in the id. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.tags.delete_tag( + id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier of a given tag + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.tags.attach_tag_to_ticket(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can tag a specific ticket. This will return a tag object for the tag that was added to the ticket. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.tags.attach_tag_to_ticket( + ticket_id="64619700005694", + id="7522907", + admin_id="780", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**ticket_id:** `str` — ticket_id + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the tag which is given by Intercom + +
+
+ +
+
+ +**admin_id:** `str` — The unique identifier for the admin which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.tags.detach_tag_from_ticket(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can remove tag from a specific ticket. This will return a tag object for the tag that was removed from the ticket. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.tags.detach_tag_from_ticket( + ticket_id="64619700005694", + id="7522907", + admin_id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**ticket_id:** `str` — ticket_id + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the tag which is given by Intercom + +
+
+ +
+
+ +**admin_id:** `str` — The unique identifier for the admin which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Conversations +
client.unstable.conversations.list_conversations(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all conversations. + +You can optionally request the result page size and the cursor to start after to fetch the result. +{% admonition type="warning" name="Pagination" %} + You can use pagination to limit the number of results returned. The default is `20` 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 %} +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.conversations.list_conversations( + per_page=1, + starting_after="starting_after", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**per_page:** `typing.Optional[int]` — How many results per page + +
+
+ +
+
+ +**starting_after:** `typing.Optional[str]` — String used to get the next page of conversations. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.conversations.create_conversation(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a conversation that has been initiated by a contact (ie. user or lead). +The conversation can be an in-app message only. + +{% admonition type="info" name="Sending for visitors" %} +You can also send a message from a visitor by specifying their `user_id` or `id` value in the `from` field, along with a `type` field value of `contact`. +This visitor will be automatically converted to a contact with a lead role once the conversation is created. +{% /admonition %} + +This will return the Message model that has been created. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.unstable.conversations import CreateConversationRequestFrom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.conversations.create_conversation( + from_=CreateConversationRequestFrom( + type="user", + id="123_doesnt_exist", + ), + body="Hello there", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**from_:** `CreateConversationRequestFrom` + +
+
+ +
+
+ +**body:** `str` — The content of the message. HTML is not supported. + +
+
+ +
+
+ +**created_at:** `typing.Optional[int]` — The time the conversation was created as a UTC Unix timestamp. If not provided, the current time will be used. This field is only recommneded for migrating past conversations from another source into Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.conversations.retrieve_conversation(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ + +You can fetch the details of a single conversation. + +This will return a single Conversation model with all its conversation parts. + +{% admonition type="warning" name="Hard limit of 500 parts" %} +The maximum number of conversation parts that can be returned via the API is 500. If you have more than that we will return the 500 most recent conversation parts. +{% /admonition %} + +For AI agent conversation metadata, please note that you need to have the agent enabled in your workspace, which is a [paid feature](https://www.intercom.com/help/en/articles/8205718-fin-resolutions#h_97f8c2e671). +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.conversations.retrieve_conversation( + id=1, + display_as="plaintext", + include_translations=True, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The id of the conversation to target + +
+
+ +
+
+ +**display_as:** `typing.Optional[str]` — Set to plaintext to retrieve conversation messages in plain text. This affects both the body and subject fields. + +
+
+ +
+
+ +**include_translations:** `typing.Optional[bool]` — If set to true, conversation parts will be translated to the detected language of the conversation. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.conversations.update_conversation(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ + +You can update an existing conversation. + +{% admonition type="info" name="Replying and other actions" %} +If you want to reply to a coveration or take an action such as assign, unassign, open, close or snooze, take a look at the reply and manage endpoints. +{% /admonition %} + +{% admonition type="info" %} + This endpoint handles both **conversation updates** and **custom object associations**. + + See _`update a conversation with an association to a custom object instance`_ in the request/response examples to see the custom object association format. +{% /admonition %} + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.conversations.update_conversation( + id=1, + display_as="plaintext", + read=True, + title="new conversation title", + custom_attributes={"issue_type": "Billing", "priority": "High"}, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The id of the conversation to target + +
+
+ +
+
+ +**display_as:** `typing.Optional[str]` — Set to plaintext to retrieve conversation messages in plain text. This affects both the body and subject fields. + +
+
+ +
+
+ +**read:** `typing.Optional[bool]` — Mark a conversation as read within Intercom. + +
+
+ +
+
+ +**title:** `typing.Optional[str]` — The title given to the conversation + +
+
+ +
+
+ +**custom_attributes:** `typing.Optional[CustomAttributes]` + +
+
+ +
+
+ +**company_id:** `typing.Optional[str]` — The ID of the company that the conversation is associated with. The unique identifier for the company which is given by Intercom. Set to nil to remove company. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.conversations.delete_conversation(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can delete a single conversation. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.conversations.delete_conversation( + id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — id + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.conversations.search_conversations(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can search for multiple conversations by the value of their attributes in order to fetch exactly which ones you want. + +To search for conversations, you need to send a `POST` request to `https://api.intercom.io/conversations/search`. + +This will accept a query object in the body which will define your filters in order to search for conversations. +{% 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 `20` results per page and maximum is `150`. + 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 %} + +### 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 + +### Accepted Fields + +Most keys listed in the conversation 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"`). +The `source.body` field is unique as the search will not be performed against the entire value, but instead against every element of the value separately. For example, when searching for a conversation with a `"I need support"` body - the query should contain a `=` operator with the value `"support"` for such conversation to be returned. A query with a `=` operator and a `"need support"` value will not yield a result. + +| Field | Type | +| :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | +| id | String | +| created_at | Date (UNIX timestamp) | +| updated_at | Date (UNIX timestamp) | +| source.type | String
Accepted fields are `conversation`, `email`, `facebook`, `instagram`, `phone_call`, `phone_switch`, `push`, `sms`, `twitter` and `whatsapp`. | +| source.id | String | +| source.delivered_as | String | +| source.subject | String | +| source.body | String | +| source.author.id | String | +| source.author.type | String | +| source.author.name | String | +| source.author.email | String | +| source.url | String | +| contact_ids | String | +| teammate_ids | String | +| admin_assignee_id | String | +| team_assignee_id | String | +| channel_initiated | String | +| open | Boolean | +| read | Boolean | +| state | String | +| waiting_since | Date (UNIX timestamp) | +| snoozed_until | Date (UNIX timestamp) | +| tag_ids | String | +| priority | String | +| statistics.time_to_assignment | Integer | +| statistics.time_to_admin_reply | Integer | +| statistics.time_to_first_close | Integer | +| statistics.time_to_last_close | Integer | +| statistics.median_time_to_reply | Integer | +| statistics.first_contact_reply_at | Date (UNIX timestamp) | +| statistics.first_assignment_at | Date (UNIX timestamp) | +| statistics.first_admin_reply_at | Date (UNIX timestamp) | +| statistics.first_close_at | Date (UNIX timestamp) | +| statistics.last_assignment_at | Date (UNIX timestamp) | +| statistics.last_assignment_admin_reply_at | Date (UNIX timestamp) | +| statistics.last_contact_reply_at | Date (UNIX timestamp) | +| statistics.last_admin_reply_at | Date (UNIX timestamp) | +| statistics.last_close_at | Date (UNIX timestamp) | +| statistics.last_closed_by_id | String | +| statistics.count_reopens | Integer | +| statistics.count_assignments | Integer | +| statistics.count_conversation_parts | Integer | +| conversation_rating.requested_at | Date (UNIX timestamp) | +| conversation_rating.replied_at | Date (UNIX timestamp) | +| conversation_rating.score | Integer | +| conversation_rating.remark | String | +| conversation_rating.contact_id | String | +| conversation_rating.admin_d | String | +| ai_agent_participated | Boolean | +| ai_agent.resolution_state | String | +| ai_agent.last_answer_type | String | +| ai_agent.rating | Integer | +| ai_agent.rating_remark | String | +| ai_agent.source_type | String | +| ai_agent.source_title | String | + +### Accepted Operators + +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 Shortcut for `OR` queries Values most be in Array | +| NIN | All | Not In Shortcut for `OR !` queries Values must be in Array | +| > | Integer Date (UNIX Timestamp) | Greater (or equal) than | +| < | Integer Date (UNIX Timestamp) | Lower (or equal) than | +| ~ | String | Contains | +| !~ | String | Doesn't Contain | +| ^ | String | Starts With | +| $ | String | Ends With | +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.unstable import ( + MultipleFilterSearchRequest, + SingleFilterSearchRequest, + StartingAfterPaging, +) + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.conversations.search_conversations( + query=MultipleFilterSearchRequest( + operator="AND", + value=[ + SingleFilterSearchRequest( + field="created_at", + operator=">", + value="1306054154", + ) + ], + ), + pagination=StartingAfterPaging( + per_page=5, + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**query:** `SearchRequestQuery` + +
+
+ +
+
+ +**pagination:** `typing.Optional[StartingAfterPaging]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.conversations.reply_conversation(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can reply to a conversation with a message from an admin or on behalf of a contact, or with a note for admins. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.unstable import ContactReplyIntercomUserIdRequest + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.conversations.reply_conversation( + id='123 or "last"', + request=ContactReplyIntercomUserIdRequest( + body="Thanks again :)", + intercom_user_id="6762f1661bb69f9f2193bbbf", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The Intercom provisioned identifier for the conversation or the string "last" to reply to the last part of the conversation + +
+
+ +
+
+ +**request:** `ReplyConversationRequestBody` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.conversations.manage_conversation(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +For managing conversations you can: +- Close a conversation +- Snooze a conversation to reopen on a future date +- Open a conversation which is `snoozed` or `closed` +- Assign a conversation to an admin and/or team. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.unstable.conversations import ManageConversationRequestBody_Close + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.conversations.manage_conversation( + id="123", + request=ManageConversationRequestBody_Close( + admin_id="12345", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The identifier for the conversation as given by Intercom. + +
+
+ +
+
+ +**request:** `ManageConversationRequestBody` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.conversations.attach_contact_to_conversation(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can add participants who are contacts to a conversation, on behalf of either another contact or an admin. + +{% admonition type="warning" name="Contacts without an email" %} +If you add a contact via the email parameter and there is no user/lead found on that workspace with he given email, then we will create a new contact with `role` set to `lead`. +{% /admonition %} + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.unstable.conversations import ( + AttachContactToConversationRequestCustomerIntercomUserId, +) + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.conversations.attach_contact_to_conversation( + id="123", + admin_id="12345", + customer=AttachContactToConversationRequestCustomerIntercomUserId( + intercom_user_id="6762f19e1bb69f9f2193bbd5", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The identifier for the conversation as given by Intercom. + +
+
+ +
+
+ +**admin_id:** `typing.Optional[str]` — The `id` of the admin who is adding the new participant. + +
+
+ +
+
+ +**customer:** `typing.Optional[AttachContactToConversationRequestCustomer]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.conversations.detach_contact_from_conversation(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can add participants who are contacts to a conversation, on behalf of either another contact or an admin. + +{% admonition type="warning" name="Contacts without an email" %} +If you add a contact via the email parameter and there is no user/lead found on that workspace with he given email, then we will create a new contact with `role` set to `lead`. +{% /admonition %} + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.conversations.detach_contact_from_conversation( + conversation_id="123", + contact_id="123", + admin_id="5017690", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**conversation_id:** `str` — The identifier for the conversation as given by Intercom. + +
+
+ +
+
+ +**contact_id:** `str` — The identifier for the contact as given by Intercom. + +
+
+ +
+
+ +**admin_id:** `str` — The `id` of the admin who is performing the action. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.conversations.redact_conversation(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can redact a conversation part or the source message of a conversation (as seen in the source object). + +{% admonition type="info" name="Redacting parts and messages" %} +If you are redacting a conversation part, it must have a `body`. If you are redacting a source message, it must have been created by a contact. We will return a `conversation_part_not_redactable` error if these criteria are not met. +{% /admonition %} + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.unstable import RedactConversationRequest_ConversationPart + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.conversations.redact_conversation( + request=RedactConversationRequest_ConversationPart( + conversation_id="really_123_doesnt_exist", + conversation_part_id="really_123_doesnt_exist", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `RedactConversationRequest` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.conversations.convert_conversation_to_ticket(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can convert a conversation to a ticket. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.conversations.convert_conversation_to_ticket( + id=1, + ticket_type_id="54", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The id of the conversation to target + +
+
+ +
+
+ +**ticket_type_id:** `str` — The ID of the type of ticket you want to convert the conversation to + +
+
+ +
+
+ +**attributes:** `typing.Optional[TicketRequestCustomAttributes]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Unstable CustomChannelEvents +
client.unstable.custom_channel_events.notify_new_conversation(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Notifies Intercom that a new conversation was created in your custom channel/platform. This triggers conversation creation and workflow automations within Intercom for your custom channel integration. +> **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.unstable import CustomChannelContact + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.custom_channel_events.notify_new_conversation( + event_id="evt_12345", + external_conversation_id="conv_67890", + contact=CustomChannelContact( + type="user", + external_id="user_001", + name="Jane Doe", + email="jane.doe@example.com", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**event_id:** `str` — Unique identifier for the event. + +
+
+ +
+
+ +**external_conversation_id:** `str` — Identifier for the conversation in your application. + +
+
+ +
+
+ +**contact:** `CustomChannelContact` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.custom_channel_events.notify_new_message(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Notifies Intercom that a new message was sent in a conversation on your custom channel/platform. This allows Intercom to process the message and trigger any relevant workflow automations. +> **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.unstable import CustomChannelContact + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.custom_channel_events.notify_new_message( + event_id="evt_54321", + external_conversation_id="conv_98765", + contact=CustomChannelContact( + type="user", + external_id="user_002", + name="John Smith", + email="john.smith@example.com", + ), + body="Hello, I need help with my order.", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**body:** `str` — The message content sent by the user. + +
+
+ +
+
+ +**event_id:** `str` — Unique identifier for the event. + +
+
+ +
+
+ +**external_conversation_id:** `str` — Identifier for the conversation in your application. + +
+
+ +
+
+ +**contact:** `CustomChannelContact` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.custom_channel_events.notify_quick_reply_selected(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Notifies Intercom that a user selected a quick reply option in your custom channel/platform. This allows Intercom to process the response and trigger any relevant workflow automations. +> **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.unstable import CustomChannelContact + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.custom_channel_events.notify_quick_reply_selected( + event_id="evt_67890", + external_conversation_id="conv_13579", + contact=CustomChannelContact( + type="user", + external_id="user_003", + name="Alice Example", + email="alice@example.com", + ), + quick_reply_option_id="1234", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**quick_reply_option_id:** `str` — Id of the selected quick reply option. + +
+
+ +
+
+ +**event_id:** `str` — Unique identifier for the event. + +
+
+ +
+
+ +**external_conversation_id:** `str` — Identifier for the conversation in your application. + +
+
+ +
+
+ +**contact:** `CustomChannelContact` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.custom_channel_events.notify_attribute_collected(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Notifies Intercom that a user provided a response to an attribute collector in your custom channel/platform. This allows Intercom to process the attribute and trigger any relevant workflow automations. +> **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.unstable import CustomChannelAttribute, CustomChannelContact + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.custom_channel_events.notify_attribute_collected( + event_id="evt_24680", + external_conversation_id="conv_11223", + contact=CustomChannelContact( + type="user", + external_id="user_004", + name="Bob Example", + email="bob@example.com", + ), + attribute=CustomChannelAttribute( + id="shipping_address", + value="123 Main St, Springfield", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**attribute:** `CustomChannelAttribute` + +
+
+ +
+
+ +**event_id:** `str` — Unique identifier for the event. + +
+
+ +
+
+ +**external_conversation_id:** `str` — Identifier for the conversation in your application. + +
+
+ +
+
+ +**contact:** `CustomChannelContact` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Custom Object Instances +
client.unstable.custom_object_instances.get_custom_object_instances_by_external_id(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Fetch a Custom Object Instance by external_id. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.custom_object_instances.get_custom_object_instances_by_external_id( + custom_object_type_identifier="Order", + external_id="external_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**custom_object_type_identifier:** `str` — The unique identifier of the custom object type that defines the structure of the custom object instance. + +
+
+ +
+
+ +**external_id:** `str` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.custom_object_instances.create_custom_object_instances(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Create or update a custom object instance +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.custom_object_instances.create_custom_object_instances( + custom_object_type_identifier="Order", + external_id="123", + external_created_at=1392036272, + external_updated_at=1392036272, + custom_attributes={ + "order_number": "ORDER-12345", + "total_amount": "custom_attributes", + }, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**custom_object_type_identifier:** `str` — The unique identifier of the custom object type that defines the structure of the custom object instance. + +
+
+ +
+
+ +**external_id:** `typing.Optional[str]` — A unique identifier for the Custom Object instance in the external system it originated from. + +
+
+ +
+
+ +**external_created_at:** `typing.Optional[int]` — The time when the Custom Object instance was created in the external system it originated from. + +
+
+ +
+
+ +**external_updated_at:** `typing.Optional[int]` — The time when the Custom Object instance was last updated in the external system it originated from. + +
+
+ +
+
+ +**custom_attributes:** `typing.Optional[typing.Dict[str, typing.Optional[str]]]` — The custom attributes which are set for the Custom Object instance. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.custom_object_instances.delete_custom_object_instances_by_id(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Delete a single Custom Object instance by external_id. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.custom_object_instances.delete_custom_object_instances_by_id( + custom_object_type_identifier="Order", + external_id="external_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**custom_object_type_identifier:** `str` — The unique identifier of the custom object type that defines the structure of the custom object instance. + +
+
+ +
+
+ +**external_id:** `str` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.custom_object_instances.get_custom_object_instances_by_id(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Fetch a Custom Object Instance by id. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.custom_object_instances.get_custom_object_instances_by_id( + custom_object_type_identifier="Order", + id="id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**custom_object_type_identifier:** `str` — The unique identifier of the custom object type that defines the structure of the custom object instance. + +
+
+ +
+
+ +**id:** `str` — The id or external_id of the custom object instance + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.custom_object_instances.delete_custom_object_instances_by_external_id(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Delete a single Custom Object instance using the Intercom defined id. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.custom_object_instances.delete_custom_object_instances_by_external_id( + custom_object_type_identifier="Order", + id="id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**custom_object_type_identifier:** `str` — The unique identifier of the custom object type that defines the structure of the custom object instance. + +
+
+ +
+
+ +**id:** `str` — The Intercom defined id of the custom object instance + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Data Attributes +
client.unstable.data_attributes.lis_data_attributes(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all data attributes belonging to a workspace for contacts, companies or conversations. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.data_attributes.lis_data_attributes( + model="contact", + include_archived=True, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**model:** `typing.Optional[LisDataAttributesRequestModel]` — Specify the data attribute model to return. + +
+
+ +
+
+ +**include_archived:** `typing.Optional[bool]` — Include archived attributes in the list. By default we return only non archived data attributes. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.data_attributes.create_data_attribute(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a data attributes for a `contact` or a `company`. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.data_attributes.create_data_attribute( + request={"key": "value"}, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `typing.Any` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.data_attributes.update_data_attribute(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ + +You can update a data attribute. + +> 🚧 Updating the data type is not possible +> +> It is currently a dangerous action to execute changing a data attribute's type via the API. You will need to update the type via the UI instead. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.data_attributes.update_data_attribute( + id=1, + request={"key": "value"}, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The data attribute id + +
+
+ +
+
+ +**request:** `typing.Any` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Data Events +
client.unstable.data_events.lis_data_events(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ + +> 🚧 +> +> Please note that you can only 'list' events that are less than 90 days old. Event counts and summaries will still include your events older than 90 days but you cannot 'list' these events individually if they are older than 90 days + +The events belonging to a customer can be listed by sending a GET request to `https://api.intercom.io/events` with a user or lead identifier along with a `type` parameter. The identifier parameter can be one of `user_id`, `email` or `intercom_user_id`. The `type` parameter value must be `user`. + +- `https://api.intercom.io/events?type=user&user_id={user_id}` +- `https://api.intercom.io/events?type=user&email={email}` +- `https://api.intercom.io/events?type=user&intercom_user_id={id}` (this call can be used to list leads) + +The `email` parameter value should be [url encoded](http://en.wikipedia.org/wiki/Percent-encoding) when sending. + +You can optionally define the result page size as well with the `per_page` parameter. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.unstable.data_events import LisDataEventsRequestFilterUserId + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.data_events.lis_data_events( + filter=LisDataEventsRequestFilterUserId( + user_id="user_id", + ), + type="type", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**filter:** `LisDataEventsRequestFilter` + +
+
+ +
+
+ +**type:** `str` — The value must be user + +
+
+ +
+
+ +**summary:** `typing.Optional[bool]` — summary flag + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.data_events.create_data_event(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ + +You will need an Access Token that has write permissions to send Events. Once you have a key you can submit events via POST to the Events resource, which is located at https://api.intercom.io/events, or you can send events using one of the client libraries. When working with the HTTP API directly a client should send the event with a `Content-Type` of `application/json`. + +When using the JavaScript API, [adding the code to your app](http://docs.intercom.io/configuring-Intercom/tracking-user-events-in-your-app) makes the Events API available. Once added, you can submit an event using the `trackEvent` method. This will associate the event with the Lead or currently logged-in user or logged-out visitor/lead and send it to Intercom. The final parameter is a map that can be used to send optional metadata about the event. + +With the Ruby client you pass a hash describing the event to `Intercom::Event.create`, or call the `track_user` method directly on the current user object (e.g. `user.track_event`). + +**NB: For the JSON object types, please note that we do not currently support nested JSON structure.** + +| Type | Description | Example | +| :-------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------- | +| String | The value is a JSON String | `"source":"desktop"` | +| Number | The value is a JSON Number | `"load": 3.67` | +| Date | The key ends with the String `_date` and the value is a [Unix timestamp](http://en.wikipedia.org/wiki/Unix_time), assumed to be in the [UTC](http://en.wikipedia.org/wiki/Coordinated_Universal_Time) timezone. | `"contact_date": 1392036272` | +| Link | The value is a HTTP or HTTPS URI. | `"article": "https://example.org/ab1de.html"` | +| Rich Link | The value is a JSON object that contains `url` and `value` keys. | `"article": {"url": "https://example.org/ab1de.html", "value":"the dude abides"}` | +| Monetary Amount | The value is a JSON object that contains `amount` and `currency` keys. The `amount` key is a positive integer representing the amount in cents. The price in the example to the right denotes €349.99. | `"price": {"amount": 34999, "currency": "eur"}` | + +**Lead Events** + +When submitting events for Leads, you will need to specify the Lead's `id`. + +**Metadata behaviour** + +- We currently limit the number of tracked metadata keys to 10 per event. Once the quota is reached, we ignore any further keys we receive. The first 10 metadata keys are determined by the order in which they are sent in with the event. +- It is not possible to change the metadata keys once the event has been sent. A new event will need to be created with the new keys and you can archive the old one. +- There might be up to 24 hrs delay when you send a new metadata for an existing event. + +**Event de-duplication** + +The API may detect and ignore duplicate events. Each event is uniquely identified as a combination of the following data - the Workspace identifier, the Contact external identifier, the Data Event name and the Data Event created time. As a result, it is **strongly recommended** to send a second granularity Unix timestamp in the `created_at` field. + +Duplicated events are responded to using the normal `202 Accepted` code - an error is not thrown, however repeat requests will be counted against any rate limit that is in place. + +### HTTP API Responses + +- Successful responses to submitted events return `202 Accepted` with an empty body. +- Unauthorised access will be rejected with a `401 Unauthorized` or `403 Forbidden` response code. +- Events sent about users that cannot be found will return a `404 Not Found`. +- Event lists containing duplicate events will have those duplicates ignored. +- Server errors will return a `500` response code and may contain an error message in the body. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.data_events.create_data_event( + request={"key": "value"}, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `CreateDataEventRequestTwo` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.data_events.data_event_summaries(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Create event summaries for a user. Event summaries are used to track the number of times an event has occurred, the first time it occurred and the last time it occurred. + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.data_events.data_event_summaries() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**user_id:** `typing.Optional[str]` — Your identifier for the user. + +
+
+ +
+
+ +**event_summaries:** `typing.Optional[CreateDataEventSummariesRequestEventSummaries]` — A list of event summaries for the user. Each event summary should contain the event name, the time the event occurred, and the number of times the event occurred. The event name should be a past tense 'verb-noun' combination, to improve readability, for example `updated-plan`. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Data Export +
client.unstable.data_export.create_data_export(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +To create your export job, you need to send a `POST` request to the export endpoint `https://api.intercom.io/export/content/data`. + +The only parameters you need to provide are the range of dates that you want exported. + +>🚧 Limit of one active job +> +> You can only have one active job per workspace. You will receive a HTTP status code of 429 with the message Exceeded rate limit of 1 pending message data export jobs if you attempt to create a second concurrent job. + +>❗️ Updated_at not included +> +> It should be noted that the timeframe only includes messages sent during the time period and not messages that were only updated during this period. For example, if a message was updated yesterday but sent two days ago, you would need to set the created_at_after date before the message was sent to include that in your retrieval job. + +>📘 Date ranges are inclusive +> +> Requesting data for 2018-06-01 until 2018-06-30 will get all data for those days including those specified - e.g. 2018-06-01 00:00:00 until 2018-06-30 23:59:99. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.data_export.create_data_export( + created_at_after=1734519776, + created_at_before=1734537776, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**created_at_after:** `int` — The start date that you request data for. It must be formatted as a unix timestamp. + +
+
+ +
+
+ +**created_at_before:** `int` — The end date that you request data for. It must be formatted as a unix timestamp. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.data_export.get_data_export(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can view the status of your job by sending a `GET` request to the URL +`https://api.intercom.io/export/content/data/{job_identifier}` - the `{job_identifier}` is the value returned in the response when you first created the export job. More on it can be seen in the Export Job Model. + +> 🚧 Jobs expire after two days +> All jobs that have completed processing (and are thus available to download from the provided URL) will have an expiry limit of two days from when the export ob completed. After this, the data will no longer be available. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.data_export.get_data_export( + job_identifier="job_identifier", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**job_identifier:** `str` — job_identifier + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.data_export.cancel_data_export(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can cancel your job +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.data_export.cancel_data_export( + job_identifier="job_identifier", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**job_identifier:** `str` — job_identifier + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.data_export.download_data_export(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +When a job has a status of complete, and thus a filled download_url, you can download your data by hitting that provided URL, formatted like so: https://api.intercom.io/download/content/data/xyz1234. + +Your exported message data will be streamed continuously back down to you in a gzipped CSV format. + +> 📘 Octet header required +> +> You will have to specify the header Accept: `application/octet-stream` when hitting this endpoint. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.data_export.download_data_export( + job_identifier="job_identifier", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**job_identifier:** `str` — job_identifier + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Jobs +
client.unstable.jobs.status(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve the status of job execution. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.jobs.status( + id="id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the job which is given by Intercom + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Macros +
client.unstable.macros.list_macros(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all macros (saved replies) in your workspace for use in automating responses. + +The macros are returned in descending order by updated_at. + +**Pagination** + +This endpoint uses cursor-based pagination via the `starting_after` parameter. The cursor is a Base64-encoded JSON array containing `[updated_at, id]` of the last item from the previous page. + +**Placeholder Transformation** + +The API transforms Intercom placeholders to a more standard XML-like format: +- From: `{{user.name | fallback: 'there'}}` +- To: `` +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.macros.list_macros( + per_page=1, + starting_after="WzE3MTk0OTM3NTcuMCwgIjEyMyJd", + updated_since=1000000, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**per_page:** `typing.Optional[int]` — The number of results per page + +
+
+ +
+
+ +**starting_after:** `typing.Optional[str]` — Base64-encoded cursor containing [updated_at, id] for pagination + +
+
+ +
+
+ +**updated_since:** `typing.Optional[int]` — Unix timestamp to filter macros updated after this time + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.macros.get_macro(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a single macro (saved reply) by its ID. The macro will only be returned if it is visible to the authenticated user based on its visibility settings. + +**Visibility Rules** + +A macro is returned based on its `visible_to` setting: +- `everyone`: Always visible to all team members +- `specific_teams`: Only visible if the authenticated user belongs to one of the teams specified in `visible_to_team_ids` + +If a macro exists but is not visible to the authenticated user, a 404 error is returned. + +**Placeholder Transformation** + +The API transforms Intercom placeholders to a more standard XML-like format in the `body` field: +- From: `{{user.name | fallback: 'there'}}` +- To: `` + +Default values in placeholders are HTML-escaped for security. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.macros.get_macro( + id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier of the macro + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Messages +
client.unstable.messages.create_message(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a message that has been initiated by an admin. The conversation can be either an in-app message, an email, sms or whatsapp. + +> 🚧 Sending for visitors +> +> There can be a short delay between when a contact is created and when a contact becomes available to be messaged through the API. A 404 Not Found error will be returned in this case. + +This will return the Message model that has been created. + +> 🚧 Retrieving Associated Conversations +> +> As this is a message, there will be no conversation present until the contact responds. Once they do, you will have to search for a contact's conversations with the id of the message. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.messages.create_message( + request={ + "from": {"type": "admin", "id": "991267821"}, + "to": {"type": "user", "id": "6762f23b1bb69f9f2193bc1d"}, + "message_type": "sms", + "body": "heyy https://picsum.photos/200/300", + }, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `typing.Optional[CreateMessageRequest]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.messages.get_whats_app_message_status(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieves statuses of messages sent from the Outbound module. Currently, this API only supports WhatsApp messages. + + +This endpoint returns paginated status events for WhatsApp messages sent via the Outbound module, providing +information about delivery state and related message details. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.messages.get_whats_app_message_status( + ruleset_id="ruleset_id", + per_page=1, + starting_after="starting_after", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**ruleset_id:** `str` — The unique identifier for the set of messages to check status for + +
+
+ +
+
+ +**per_page:** `typing.Optional[int]` — Number of results per page (default 50, max 100) + +
+
+ +
+
+ +**starting_after:** `typing.Optional[str]` — Cursor for pagination, used to fetch the next page of results + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## News +
client.unstable.news.list_news_items() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all news items +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.news.list_news_items() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.news.create_news_item(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a news item +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.unstable.news import NewsfeedAssignment + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.news.create_news_item( + title="Halloween is here!", + body="

New costumes in store for this spooky season

", + sender_id=991267834, + state="live", + deliver_silently=True, + labels=["Product", "Update", "New"], + reactions=["😆", "😅"], + newsfeed_assignments=[ + NewsfeedAssignment( + newsfeed_id=53, + published_at=1664638214, + ) + ], +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**title:** `str` — The title of the news item. + +
+
+ +
+
+ +**sender_id:** `int` — The id of the sender of the news item. Must be a teammate on the workspace. + +
+
+ +
+
+ +**body:** `typing.Optional[str]` — The news item body, which may contain HTML. + +
+
+ +
+
+ +**state:** `typing.Optional[NewsItemRequestState]` — News items will not be visible to your users in the assigned newsfeeds until they are set live. + +
+
+ +
+
+ +**deliver_silently:** `typing.Optional[bool]` — When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + +
+
+ +
+
+ +**labels:** `typing.Optional[typing.Sequence[str]]` — Label names displayed to users to categorize the news item. + +
+
+ +
+
+ +**reactions:** `typing.Optional[typing.Sequence[typing.Optional[str]]]` — Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + +
+
+ +
+
+ +**newsfeed_assignments:** `typing.Optional[typing.Sequence[NewsfeedAssignment]]` — A list of newsfeed_assignments to assign to the specified newsfeed. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.news.retrieve_news_item(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single news item. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.news.retrieve_news_item( + id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The unique identifier for the news item which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.news.update_news_item(...) +
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.news.update_news_item( + id=1, + title="Christmas is here!", + body="

New gifts in store for the jolly season

", + sender_id=991267848, + reactions=["😝", "😂"], +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The unique identifier for the news item which is given by Intercom. + +
+
+ +
+
+ +**title:** `str` — The title of the news item. + +
+
+ +
+
+ +**sender_id:** `int` — The id of the sender of the news item. Must be a teammate on the workspace. + +
+
+ +
+
+ +**body:** `typing.Optional[str]` — The news item body, which may contain HTML. + +
+
+ +
+
+ +**state:** `typing.Optional[NewsItemRequestState]` — News items will not be visible to your users in the assigned newsfeeds until they are set live. + +
+
+ +
+
+ +**deliver_silently:** `typing.Optional[bool]` — When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + +
+
+ +
+
+ +**labels:** `typing.Optional[typing.Sequence[str]]` — Label names displayed to users to categorize the news item. + +
+
+ +
+
+ +**reactions:** `typing.Optional[typing.Sequence[typing.Optional[str]]]` — Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + +
+
+ +
+
+ +**newsfeed_assignments:** `typing.Optional[typing.Sequence[NewsfeedAssignment]]` — A list of newsfeed_assignments to assign to the specified newsfeed. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.news.delete_news_item(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can delete a single news item. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.news.delete_news_item( + id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The unique identifier for the news item which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.news.list_live_newsfeed_items(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all news items that are live on a given newsfeed +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.news.list_live_newsfeed_items( + id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the news feed item which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.news.list_newsfeeds() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all newsfeeds +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.news.list_newsfeeds() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.news.retrieve_newsfeed(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single newsfeed +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.news.retrieve_newsfeed( + id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the news feed item which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Segments +
client.unstable.segments.list_segments(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch a list of all segments. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.segments.list_segments( + include_count=True, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**include_count:** `typing.Optional[bool]` — It includes the count of contacts that belong to each segment. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.segments.retrieve_segment(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single segment. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.segments.retrieve_segment( + id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identified of a given segment. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Switch +
client.unstable.switch.create_phone_switch(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can use the API to deflect phone calls to the Intercom Messenger. +Calling this endpoint will send an SMS with a link to the Messenger to the phone number specified. + +If custom attributes are specified, they will be added to the user or lead's custom data attributes. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.switch.create_phone_switch( + request={"key": "value"}, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `typing.Any` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Calls +
client.unstable.calls.list_calls(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve a paginated list of calls. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.calls.list_calls( + page=1, + per_page=1, +) + +``` +
+
+
+
+ +#### ⚙️ 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 25. Max 25. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.calls.show_call(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve a single call by id. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.calls.show_call( + id="id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The id of the call to retrieve + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.calls.show_call_recording(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Redirects to a signed URL for the call's recording if it exists. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.calls.show_call_recording( + id="id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The id of the call + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.calls.show_call_transcript(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Returns the transcript for the specified call as a downloadable text file. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.calls.show_call_transcript( + id="id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The id of the call + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.calls.list_calls_with_transcripts(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve calls by a list of conversation ids and include transcripts when available. +A maximum of 20 `conversation_ids` can be provided. If none are provided or more than 20 are provided, a 400 error is returned. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.calls.list_calls_with_transcripts( + conversation_ids=["64619700005694", "64619700005695"], +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**conversation_ids:** `typing.Sequence[str]` — A list of conversation ids to fetch calls for. Maximum 20. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.calls.register_fin_voice_call(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Register a Fin Voice call with Intercom. This endpoint creates an external reference +that links an external call identifier to an Intercom call and conversation. + +The call can be from different sources: +- AWS Connect (default) +- Five9 +- Zoom Phone +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.unstable import RegisterFinVoiceCallRequest + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.calls.register_fin_voice_call( + request=RegisterFinVoiceCallRequest( + phone_number="+1234567890", + call_id="call-123-abc", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `typing.Optional[RegisterFinVoiceCallRequest]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.calls.collect_fin_voice_call_by_id(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve information about a Fin Voice call using the external reference ID. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.calls.collect_fin_voice_call_by_id( + id=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — The external reference ID + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.calls.collect_fin_voice_call_by_external_id(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve information about a Fin Voice call using the external call identifier. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.calls.collect_fin_voice_call_by_external_id( + external_id="external_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**external_id:** `str` — The external call identifier from the call provider + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.calls.collect_fin_voice_call_by_phone_number(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve information about a Fin Voice call using the phone number. + +Returns the most recent matched call for the given phone number, ordered by creation date. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.calls.collect_fin_voice_call_by_phone_number( + phone_number="phone_number", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**phone_number:** `str` — Phone number in E.164 format + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Teams +
client.unstable.teams.list_teams() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +This will return a list of team objects for the App. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.teams.list_teams() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.teams.retrieve_team(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single team, containing an array of admins that belong to this team. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.teams.retrieve_team( + id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier of a given team. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Ticket States +
client.unstable.ticket_states.list_ticket_states() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can get a list of all ticket states for a workspace. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.ticket_states.list_ticket_states() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Ticket Type Attributes +
client.unstable.ticket_type_attributes.create_ticket_type_attribute(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a new attribute for a ticket type. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.ticket_type_attributes.create_ticket_type_attribute( + ticket_type_id="ticket_type_id", + name="Attribute Title", + description="Attribute Description", + data_type="string", + required_to_create=False, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**ticket_type_id:** `str` — The unique identifier for the ticket type which is given by Intercom. + +
+
+ +
+
+ +**name:** `str` — The name of the ticket type attribute + +
+
+ +
+
+ +**description:** `str` — The description of the attribute presented to the teammate or contact + +
+
+ +
+
+ +**data_type:** `CreateTicketTypeAttributeRequestDataType` — The data type of the attribute + +
+
+ +
+
+ +**required_to_create:** `typing.Optional[bool]` — Whether the attribute is required to be filled in when teammates are creating the ticket in Inbox. + +
+
+ +
+
+ +**required_to_create_for_contacts:** `typing.Optional[bool]` — Whether the attribute is required to be filled in when contacts are creating the ticket in Messenger. + +
+
+ +
+
+ +**visible_on_create:** `typing.Optional[bool]` — Whether the attribute is visible to teammates when creating a ticket in Inbox. + +
+
+ +
+
+ +**visible_to_contacts:** `typing.Optional[bool]` — Whether the attribute is visible to contacts when creating a ticket in Messenger. + +
+
+ +
+
+ +**multiline:** `typing.Optional[bool]` — Whether the attribute allows multiple lines of text (only applicable to string attributes) + +
+
+ +
+
+ +**list_items:** `typing.Optional[str]` — A comma delimited list of items for the attribute value (only applicable to list attributes) + +
+
+ +
+
+ +**allow_multiple_values:** `typing.Optional[bool]` — Whether the attribute allows multiple files to be attached to it (only applicable to file attributes) + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.ticket_type_attributes.update_ticket_type_attribute(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can update an existing attribute for a ticket type. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.ticket_type_attributes.update_ticket_type_attribute( + ticket_type_id="ticket_type_id", + id="id", + description="New Attribute Description", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**ticket_type_id:** `str` — The unique identifier for the ticket type which is given by Intercom. + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the ticket type attribute which is given by Intercom. + +
+
+ +
+
+ +**name:** `typing.Optional[str]` — The name of the ticket type attribute + +
+
+ +
+
+ +**description:** `typing.Optional[str]` — The description of the attribute presented to the teammate or contact + +
+
+ +
+
+ +**required_to_create:** `typing.Optional[bool]` — Whether the attribute is required to be filled in when teammates are creating the ticket in Inbox. + +
+
+ +
+
+ +**required_to_create_for_contacts:** `typing.Optional[bool]` — Whether the attribute is required to be filled in when contacts are creating the ticket in Messenger. + +
+
+ +
+
+ +**visible_on_create:** `typing.Optional[bool]` — Whether the attribute is visible to teammates when creating a ticket in Inbox. + +
+
+ +
+
+ +**visible_to_contacts:** `typing.Optional[bool]` — Whether the attribute is visible to contacts when creating a ticket in Messenger. + +
+
+ +
+
+ +**multiline:** `typing.Optional[bool]` — Whether the attribute allows multiple lines of text (only applicable to string attributes) + +
+
+ +
+
+ +**list_items:** `typing.Optional[str]` — A comma delimited list of items for the attribute value (only applicable to list attributes) + +
+
+ +
+
+ +**allow_multiple_values:** `typing.Optional[bool]` — Whether the attribute allows multiple files to be attached to it (only applicable to file attributes) + +
+
+ +
+
+ +**archived:** `typing.Optional[bool]` — Whether the attribute should be archived and not shown during creation of the ticket (it will still be present on previously created tickets) + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Ticket Types +
client.unstable.ticket_types.list_ticket_types() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can get a list of all ticket types for a workspace. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.ticket_types.list_ticket_types() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.ticket_types.create_ticket_type(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can create a new ticket type. +> 📘 Creating ticket types. +> +> Every ticket type will be created with two default attributes: _default_title_ and _default_description_. +> For the `icon` propery, use an emoji from [Twemoji Cheatsheet](https://twemoji-cheatsheet.vercel.app/) +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.ticket_types.create_ticket_type( + request={"key": "value"}, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `typing.Any` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.ticket_types.get_ticket_type(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single ticket type. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.ticket_types.get_ticket_type( + id="id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the ticket type which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Tickets +
client.unstable.tickets.reply_ticket(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can reply to a ticket with a message from an admin or on behalf of a contact, or with a note for admins. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.unstable import ContactReplyTicketIntercomUserIdRequest + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.tickets.reply_ticket( + id="123", + request=ContactReplyTicketIntercomUserIdRequest( + body="Thanks again :)", + intercom_user_id="6762f2a41bb69f9f2193bc4c", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` + +
+
+ +
+
+ +**request:** `ReplyTicketRequestBody` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.tickets.enqueue_create_ticket(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Enqueues ticket creation for asynchronous processing, returning if the job was enqueued successfully to be processed. We attempt to perform a best-effort validation on inputs before tasks are enqueued. If the given parameters are incorrect, we won't enqueue the job. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.unstable import CreateTicketRequestContactsItemId + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.tickets.enqueue_create_ticket( + ticket_type_id="1234", + contacts=[ + CreateTicketRequestContactsItemId( + id="6762f2d81bb69f9f2193bc54", + ) + ], +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**ticket_type_id:** `str` — The ID of the type of ticket you want to create + +
+
+ +
+
+ +**contacts:** `typing.Sequence[CreateTicketRequestContactsItem]` — The list of contacts (users or leads) affected by this ticket. Currently only one is allowed + +
+
+ +
+
+ +**skip_notifications:** `typing.Optional[bool]` — Option to disable notifications when a Ticket is created. + +
+
+ +
+
+ +**conversation_to_link_id:** `typing.Optional[str]` + +The ID of the conversation you want to link to the ticket. Here are the valid ways of linking two tickets: + - conversation | back-office ticket + - customer tickets | non-shared back-office ticket + - conversation | tracker ticket + - customer ticket | tracker ticket + +
+
+ +
+
+ +**company_id:** `typing.Optional[str]` — The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom + +
+
+ +
+
+ +**created_at:** `typing.Optional[int]` — The time the ticket was created. If not provided, the current time will be used. + +
+
+ +
+
+ +**assignment:** `typing.Optional[CreateTicketRequestAssignment]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.tickets.get_ticket(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single ticket. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.tickets.get_ticket( + id="id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the ticket which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.tickets.update_ticket(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can update a ticket. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.tickets.update_ticket( + id="id", + ticket_state_id="123", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the ticket which is given by Intercom + +
+
+ +
+
+ +**ticket_attributes:** `typing.Optional[typing.Dict[str, typing.Any]]` — The attributes set on the ticket. + +
+
+ +
+
+ +**ticket_state_id:** `typing.Optional[str]` — The ID of the ticket state associated with the ticket type. + +
+
+ +
+
+ +**company_id:** `typing.Optional[str]` — The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom. Set to nil to remove company. + +
+
+ +
+
+ +**open:** `typing.Optional[bool]` — Specify if a ticket is open. Set to false to close a ticket. Closing a ticket will also unsnooze it. + +
+
+ +
+
+ +**is_shared:** `typing.Optional[bool]` — Specify whether the ticket is visible to users. + +
+
+ +
+
+ +**snoozed_until:** `typing.Optional[int]` — The time you want the ticket to reopen. + +
+
+ +
+
+ +**admin_id:** `typing.Optional[int]` — The ID of the admin performing ticket update. Needed for workflows execution and attributing actions to specific admins. + +
+
+ +
+
+ +**assignee_id:** `typing.Optional[str]` — The ID of the admin or team to which the ticket is assigned. Set this 0 to unassign it. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.tickets.delete_ticket(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can delete a ticket using the Intercom provided ID. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.tickets.delete_ticket( + id="id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier for the ticket which is given by Intercom. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.tickets.search_tickets(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can search for multiple tickets by the value of their attributes in order to fetch exactly which ones you want. + +To search for tickets, you send a `POST` request to `https://api.intercom.io/tickets/search`. + +This will accept a query object in the body which will define your filters. +{% 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 `20` 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 %} + +### 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 multiples 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 + +### Accepted Fields + +Most keys listed as part of the Ticket 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 `"foobar"`). +The `source.body` field is unique as the search will not be performed against the entire value, but instead against every element of the value separately. For example, when searching for a conversation with a `"I need support"` body - the query should contain a `=` operator with the value `"support"` for such conversation to be returned. A query with a `=` operator and a `"need support"` value will not yield a result. + +| Field | Type | +| :---------------------------------------- | :--------------------------------------------------------------------------------------- | +| id | String | +| created_at | Date (UNIX timestamp) | +| updated_at | Date (UNIX timestamp) | +| title | String | +| description | String | +| category | String | +| ticket_type_id | String | +| contact_ids | String | +| teammate_ids | String | +| admin_assignee_id | String | +| team_assignee_id | String | +| open | Boolean | +| state | String | +| snoozed_until | Date (UNIX timestamp) | +| ticket_attribute.{id} | String or Boolean or Date (UNIX timestamp) or Float or Integer | + +{% admonition type="info" name="Searching by Category" %} +When searching for tickets by the **`category`** field, specific terms must be used instead of the category names: +* For **Customer** category tickets, use the term `request`. +* For **Back-office** category tickets, use the term `task`. +* For **Tracker** category tickets, use the term `tracker`. +{% /admonition %} + +### Accepted Operators + +{% admonition type="info" name="Searching based on `created_at`" %} + You may 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 Shortcut for `OR` queries Values most be in Array | +| NIN | All | Not In Shortcut for `OR !` queries Values must be in Array | +| > | Integer Date (UNIX Timestamp) | Greater (or equal) than | +| < | Integer Date (UNIX Timestamp) | Lower (or equal) than | +| ~ | String | Contains | +| !~ | String | Doesn't Contain | +| ^ | String | Starts With | +| $ | String | Ends With | +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom +from intercom.unstable import ( + MultipleFilterSearchRequest, + SingleFilterSearchRequest, + StartingAfterPaging, +) + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.tickets.search_tickets( + query=MultipleFilterSearchRequest( + operator="AND", + value=[ + SingleFilterSearchRequest( + field="created_at", + operator=">", + value="1306054154", + ) + ], + ), + pagination=StartingAfterPaging( + per_page=5, + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**query:** `SearchRequestQuery` + +
+
+ +
+
+ +**pagination:** `typing.Optional[StartingAfterPaging]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Visitors +
client.unstable.visitors.retrieve_visitor_with_user_id(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can fetch the details of a single visitor. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.visitors.retrieve_visitor_with_user_id( + user_id="user_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**user_id:** `str` — The user_id of the Visitor you want to retrieve. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.visitors.update_visitor(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Sending a PUT request to `/visitors` will result in an update of an existing Visitor. + +**Option 1.** You can update a visitor by passing in the `user_id` of the visitor in the Request body. + +**Option 2.** You can update a visitor by passing in the `id` of the visitor in the Request body. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.visitors.update_visitor( + request={"user_id": "fail", "name": "Christian Fail"}, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request:** `UpdateVisitorRequestOne` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.visitors.convert_visitor(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +You can merge a Visitor to a Contact of role type `lead` or `user`. + +> 📘 What happens upon a visitor being converted? +> +> If the User exists, then the Visitor will be merged into it, the Visitor deleted and the User returned. If the User does not exist, the Visitor will be converted to a User, with the User identifiers replacing it's Visitor identifiers. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.visitors.convert_visitor( + type="user", + user={"email": "foo@bar.com"}, + visitor={"user_id": "3ecf64d0-9ed1-4e9f-88e1-da7d6e6782f3"}, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**type:** `str` — Represents the role of the Contact model. Accepts `lead` or `user`. + +
+
+ +
+
+ +**user:** `typing.Any` + +
+
+ +
+
+ +**visitor:** `typing.Any` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Brands +
client.unstable.brands.list_brands() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieves all brands for the workspace, including the default brand. +The default brand id always matches the workspace +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.brands.list_brands() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.brands.retrieve_brand(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Fetches a specific brand by its unique identifier +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.brands.retrieve_brand( + id="id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier of the brand + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Emails +
client.unstable.emails.list_emails() +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Lists all sender email address settings for the workspace +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.emails.list_emails() + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.unstable.emails.retrieve_email(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Fetches a specific email setting by its unique identifier +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from intercom import Intercom + +client = Intercom( + token="YOUR_TOKEN", +) +client.unstable.emails.retrieve_email( + id="id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `str` — The unique identifier of the email setting + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ diff --git a/requirements.txt b/requirements.txt index 571df20b..e80f640a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,4 @@ -# -# Runtime dependencies. -# -certifi -inflection==0.3.0 -requests==2.6.0 -urllib3==1.10.2 -six==1.9.0 +httpx>=0.21.2 +pydantic>= 1.9.2 +pydantic-core>=2.18.2 +typing_extensions>= 4.0.0 diff --git a/rtd-requirements.txt b/rtd-requirements.txt deleted file mode 100644 index d77bb2a7..00000000 --- a/rtd-requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -certifi -inflection==0.3.0 -requests==2.6.0 -urllib3==1.10.2 -six==1.9.0 -sphinx-rtd-theme==0.1.7 \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 7af6c2de..00000000 --- a/setup.py +++ /dev/null @@ -1,36 +0,0 @@ -# -# Copyright 2012 John Keyes -# -# http://jkeyes.mit-license.org/ -# - -import os -import re - -from setuptools import find_packages -from setuptools import setup - -with open(os.path.join('intercom', '__init__.py')) as init: - source = init.read() - m = re.search("__version__ = '(\d+\.\d+(\.(\d+|[a-z]+))?)'", source, re.M) - __version__ = m.groups()[0] - -with open('README.rst') as readme: - long_description = readme.read() - -setup( - name="python-intercom", - version=__version__, - description="Intercom API wrapper", - long_description=long_description, - author="John Keyes", - author_email="john@keyes.ie", - license="MIT License", - url="http://github.com/jkeyes/python-intercom", - keywords='Intercom crm python', - classifiers=[], - packages=find_packages(), - include_package_data=True, - install_requires=["requests", "inflection", "certifi", "six"], - zip_safe=False -) diff --git a/src/intercom/__init__.py b/src/intercom/__init__.py new file mode 100644 index 00000000..1332dd67 --- /dev/null +++ b/src/intercom/__init__.py @@ -0,0 +1,1330 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + ActivityLog, + ActivityLogActivityType, + ActivityLogList, + ActivityLogMetadata, + ActivityLogMetadataTeam, + ActivityLogPerformedBy, + AddressableList, + AdminList, + AdminPriorityLevel, + AdminReplyConversationRequest, + AdminReplyConversationRequestMessageType, + AdminReplyTicketRequest, + AdminReplyTicketRequestMessageType, + AdminReplyTicketRequestReplyOptionsItem, + AdminWithApp, + AdminWithAppAvatar, + App, + ArticleContent, + ArticleContentState, + ArticleList, + ArticleStatistics, + ArticleTranslatedContent, + AssignConversationRequest, + AssignConversationRequestType, + AwayStatusReason, + CallList, + CloseConversationRequest, + CollectionList, + CompanyAttachedContacts, + CompanyAttachedSegments, + CompanyData, + CompanyList, + CompanyScroll, + ContactArchived, + ContactAttachedCompanies, + ContactBlocked, + ContactCompanies, + ContactDeleted, + ContactList, + ContactLocation, + ContactNotes, + ContactReference, + ContactReplyBaseRequest, + ContactReplyBaseRequestReplyOptionsItem, + ContactReplyConversationRequest, + ContactReplyEmailRequest, + ContactReplyIntercomUserIdRequest, + ContactReplyTicketEmailRequest, + ContactReplyTicketIntercomUserIdRequest, + ContactReplyTicketRequest, + ContactReplyTicketUserIdRequest, + ContactReplyUserIdRequest, + ContactSegments, + ContactSocialProfiles, + ContactSubscriptionTypes, + ContactTags, + ContactUnarchived, + ContentSourcesList, + ConversationAttachmentFiles, + ConversationAttributeUpdatedByAdmin, + ConversationAttributeUpdatedByAdminAttribute, + ConversationAttributeUpdatedByAdminValue, + ConversationAttributeUpdatedByWorkflow, + ConversationAttributeUpdatedByWorkflowAttribute, + ConversationAttributeUpdatedByWorkflowValue, + ConversationAttributeUpdatedByWorkflowWorkflow, + ConversationContacts, + ConversationDeleted, + ConversationFirstContactReply, + ConversationList, + ConversationPart, + ConversationPartAuthor, + ConversationPartMetadata, + ConversationPartMetadataQuickReplyOptionsItem, + ConversationPartState, + ConversationParts, + ConversationRating, + ConversationResponseTime, + ConversationSource, + ConversationSourceType, + ConversationStatistics, + ConversationTeammates, + CreateArticleRequest, + CreateArticleRequestParentType, + CreateArticleRequestState, + CreateContactRequest, + CreateContactRequestTwo, + CreateContactRequestWithEmail, + CreateContactRequestWithExternalId, + CreateContactRequestWithRole, + CreateDataAttributeRequest, + CreateDataAttributeRequestOne, + CreateDataAttributeRequestOneDataType, + CreateDataAttributeRequestOptions, + CreateDataAttributeRequestOptionsOptionsItem, + CreateDataEventRequest, + CreateDataEventRequestTwo, + CreateDataEventRequestWithEmail, + CreateDataEventRequestWithId, + CreateDataEventRequestWithUserId, + CreateInternalArticleRequest, + CreateMessageRequest, + CreateMessageRequestFrom, + CreateMessageRequestTo, + CreateMessageRequestType, + CreateMessageRequestWithEmail, + CreateMessageRequestWithInapp, + CreateMessageRequest_Email, + CreateMessageRequest_Inapp, + CreateOrUpdateCompanyRequest, + CreateOrUpdateTagRequest, + CreatePhoneSwitchRequest, + CreateTicketReplyWithCommentRequest, + CreateTicketRequestAssignment, + CreateTicketRequestBody, + CreateTicketRequestContactsItem, + CreateTicketRequestContactsItemEmail, + CreateTicketRequestContactsItemExternalId, + CreateTicketRequestContactsItemId, + CreateTicketTypeRequest, + CreateTicketTypeRequestCategory, + CursorPages, + CustomActionFinished, + CustomActionFinishedAction, + CustomActionFinishedActionResult, + CustomActionStarted, + CustomActionStartedAction, + CustomAttributes, + CustomAttributesValue, + CustomChannelAttribute, + CustomChannelBaseEvent, + CustomChannelContact, + CustomChannelContactType, + CustomChannelNotificationResponse, + CustomObjectInstanceDeleted, + CustomObjectInstanceList, + CustomerRequest, + CustomerRequestEmail, + CustomerRequestIntercomUserId, + CustomerRequestUserId, + DataAttributeList, + DataEventList, + DataEventListPages, + DataEventSummary, + DataEventSummaryItem, + DataExportCsv, + Datetime, + DeletedArticleObject, + DeletedCollectionObject, + DeletedCompanyObject, + DeletedInternalArticleObject, + DeletedObject, + EmailAddressHeader, + EmailMessageMetadata, + Error, + ErrorErrorsItem, + EventDetails, + FileAttribute, + GroupContent, + GroupTranslatedContent, + InternalArticleList, + LinkedObject, + LinkedObjectCategory, + LinkedObjectList, + LinkedObjectType, + Metadata, + MultipleFilterSearchRequest, + MultipleFilterSearchRequestOperator, + MultipleFilterSearchRequestValue, + NewsItemRequest, + NewsItemRequestState, + NotFoundErrorBody, + NotFoundErrorBodyErrorsItem, + NoteList, + OffsetPages, + OpenConversationRequest, + OperatorWorkflowEvent, + OperatorWorkflowEventEvent, + OperatorWorkflowEventWorkflow, + PagesLink, + PaginatedResponse, + PaginatedResponseDataItem, + PaginatedResponseDataItem_NewsItem, + PaginatedResponseDataItem_Newsfeed, + PaginatedResponseType, + PartAttachment, + PhoneSwitch, + QuickReplyOption, + Recipient, + RecipientType, + RedactConversationRequest, + RedactConversationRequestConversationPart, + RedactConversationRequestSource, + RedactConversationRequest_ConversationPart, + RedactConversationRequest_Source, + Reference, + ReplyConversationRequest, + SearchRequest, + SearchRequestQuery, + SegmentList, + SingleFilterSearchRequest, + SingleFilterSearchRequestOperator, + SingleFilterSearchRequestValue, + SingleFilterSearchRequestValueTwoItem, + SlaApplied, + SlaAppliedSlaStatus, + SnoozeConversationRequest, + SocialProfile, + StartingAfterPaging, + SubscriptionTypeList, + TagCompanyRequest, + TagCompanyRequestCompaniesItem, + TagList, + TagMultipleUsersRequest, + TagMultipleUsersRequestUsersItem, + Tags, + TeamList, + TeamPriorityLevel, + TicketCustomAttributes, + TicketList, + TicketPartAuthor, + TicketPartAuthorType, + TicketParts, + TicketReply, + TicketReplyPartType, + TicketRequestCustomAttributes, + TicketStateList, + TicketTypeAttribute, + TicketTypeAttributeDataType, + TicketTypeAttributeList, + TicketTypeList, + Translation, + UntagCompanyRequest, + UntagCompanyRequestCompaniesItem, + UpdateArticleRequestBody, + UpdateArticleRequestBodyParentType, + UpdateCompanyRequestBody, + UpdateDataAttributeRequestBody, + UpdateDataAttributeRequestOptions, + UpdateDataAttributeRequestOptionsOptionsItem, + UpdateVisitorRequest, + UpdateVisitorRequestOne, + UpdateVisitorRequestWithId, + UpdateVisitorRequestWithUserId, + Visitor, + VisitorAvatar, + VisitorCompanies, + VisitorDeletedObject, + VisitorLocationData, + VisitorSegments, + VisitorSocialProfiles, + VisitorTags, + VisitorTagsTagsItem, + WhatsappMessageStatusList, + WhatsappMessageStatusListEventsItem, + WhatsappMessageStatusListEventsItemStatus, + WhatsappMessageStatusListPages, + WhatsappMessageStatusListPagesNext, + ) + from .errors import ( + BadRequestError, + ForbiddenError, + NotFoundError, + TooManyRequestsError, + UnauthorizedError, + UnprocessableEntityError, + ) + from . import ( + admins, + ai_agent, + ai_content, + ai_content_source, + articles, + away_status_reasons, + calls, + companies, + contacts, + conversations, + custom_channel_events, + custom_object_instances, + data_attributes, + data_events, + data_export, + events, + export, + help_center, + help_centers, + internal_articles, + jobs, + messages, + news, + notes, + phone_call_redirects, + segments, + subscription_types, + tags, + teams, + ticket_states, + ticket_types, + tickets, + unstable, + visitors, + ) + from .admins import Admin, AdminAvatar + from .ai_agent import AiAgent, AiAgentLastAnswerType, AiAgentResolutionState, AiAgentSourceType + from .ai_content import ( + ContentImportSource, + ContentImportSourceStatus, + ContentImportSourceSyncBehavior, + ContentImportSourcesList, + CreateContentImportSourceRequestStatus, + ExternalPage, + ExternalPagesList, + UpdateContentImportSourceRequestStatus, + UpdateContentImportSourceRequestSyncBehavior, + ) + from .ai_content_source import ContentSource + from .articles import ( + Article, + ArticleListItem, + ArticleListItemState, + ArticleSearchHighlights, + ArticleSearchHighlightsHighlightedSummaryItemItem, + ArticleSearchHighlightsHighlightedSummaryItemItemType, + ArticleSearchHighlightsHighlightedTitleItem, + ArticleSearchHighlightsHighlightedTitleItemType, + ArticleSearchResponse, + ArticleSearchResponseData, + InternalArticle, + UpdateArticleRequestState, + ) + from .calls import Call, ListCallsWithTranscriptsResponse, ListCallsWithTranscriptsResponseDataItem + from .client import AsyncIntercom, Intercom + from .companies import ( + CompaniesRetrieveResponse, + CompaniesRetrieveResponse_Company, + CompaniesRetrieveResponse_List, + Company, + CompanyPlan, + CompanySegments, + CompanyTags, + ) + from .contacts import ( + Contact, + ContactsCreateResponse, + ContactsFindResponse, + ContactsMergeLeadInUserResponse, + ContactsUpdateResponse, + ShowContactByExternalIdResponse, + ) + from .conversations import ( + AttachContactToConversationRequestCustomer, + AttachContactToConversationRequestCustomerCustomer, + AttachContactToConversationRequestCustomerIntercomUserId, + AttachContactToConversationRequestCustomerUserId, + Conversation, + ConversationPriority, + ConversationState, + ConversationsManageRequestBody, + ConversationsManageRequestBody_Assignment, + ConversationsManageRequestBody_Close, + ConversationsManageRequestBody_Open, + ConversationsManageRequestBody_Snoozed, + CreateConversationRequestFrom, + CreateConversationRequestFromType, + ) + from .custom_object_instances import CustomObjectInstance + from .data_attributes import ( + DataAttribute, + DataAttributeDataType, + DataAttributeModel, + DataAttributesListRequestModel, + ) + from .data_events import DataEvent + from .data_export import DataExport, DataExportExportReportingDataResponse, DataExportStatus + from .environment import IntercomEnvironment + from .events import CreateDataEventSummariesRequestEventSummaries + from .export import ( + GetExportReportingDataGetDatasetsResponse, + GetExportReportingDataGetDatasetsResponseDataItem, + GetExportReportingDataGetDatasetsResponseDataItemAttributesItem, + PostExportReportingDataEnqueueResponse, + ) + from .help_center import Collection, HelpCenter, HelpCenterList + from .internal_articles import ( + InternalArticleListItem, + InternalArticleSearchResponse, + InternalArticleSearchResponseData, + ) + from .jobs import Jobs, JobsStatus + from .messages import Message, MessageMessageType + from .news import NewsItem, NewsItemState, Newsfeed, NewsfeedAssignment + from .notes import Note, NoteContact + from .segments import Segment, SegmentPersonType + from .subscription_types import ( + SubscriptionType, + SubscriptionTypeConsentType, + SubscriptionTypeContentTypesItem, + SubscriptionTypeState, + ) + from .tags import Tag, TagBasic, TagsCreateRequestBody + from .teams import Team + from .ticket_types import UpdateTicketTypeRequestCategory + from .tickets import ( + DeleteTicketResponse, + Ticket, + TicketCategory, + TicketContacts, + TicketPart, + TicketPartPreviousTicketState, + TicketPartTicketState, + TicketPartUpdatedAttributeData, + TicketPartUpdatedAttributeDataAttribute, + TicketPartUpdatedAttributeDataValue, + TicketPartUpdatedAttributeDataValueId, + TicketPartUpdatedAttributeDataValueLabel, + TicketState, + TicketStateCategory, + TicketStateDetailed, + TicketStateDetailedCategory, + TicketStateDetailedTicketTypes, + TicketType, + TicketTypeCategory, + TicketTypeTicketStates, + TicketsReplyRequestBody, + ) + from .version import __version__ + from .visitors import ( + ConvertVisitorRequestUser, + ConvertVisitorRequestVisitor, + UserWithId, + UserWithUserId, + VisitorWithEmail, + VisitorWithId, + VisitorWithUserId, + ) +_dynamic_imports: typing.Dict[str, str] = { + "ActivityLog": ".types", + "ActivityLogActivityType": ".types", + "ActivityLogList": ".types", + "ActivityLogMetadata": ".types", + "ActivityLogMetadataTeam": ".types", + "ActivityLogPerformedBy": ".types", + "AddressableList": ".types", + "Admin": ".admins", + "AdminAvatar": ".admins", + "AdminList": ".types", + "AdminPriorityLevel": ".types", + "AdminReplyConversationRequest": ".types", + "AdminReplyConversationRequestMessageType": ".types", + "AdminReplyTicketRequest": ".types", + "AdminReplyTicketRequestMessageType": ".types", + "AdminReplyTicketRequestReplyOptionsItem": ".types", + "AdminWithApp": ".types", + "AdminWithAppAvatar": ".types", + "AiAgent": ".ai_agent", + "AiAgentLastAnswerType": ".ai_agent", + "AiAgentResolutionState": ".ai_agent", + "AiAgentSourceType": ".ai_agent", + "App": ".types", + "Article": ".articles", + "ArticleContent": ".types", + "ArticleContentState": ".types", + "ArticleList": ".types", + "ArticleListItem": ".articles", + "ArticleListItemState": ".articles", + "ArticleSearchHighlights": ".articles", + "ArticleSearchHighlightsHighlightedSummaryItemItem": ".articles", + "ArticleSearchHighlightsHighlightedSummaryItemItemType": ".articles", + "ArticleSearchHighlightsHighlightedTitleItem": ".articles", + "ArticleSearchHighlightsHighlightedTitleItemType": ".articles", + "ArticleSearchResponse": ".articles", + "ArticleSearchResponseData": ".articles", + "ArticleStatistics": ".types", + "ArticleTranslatedContent": ".types", + "AssignConversationRequest": ".types", + "AssignConversationRequestType": ".types", + "AsyncIntercom": ".client", + "AttachContactToConversationRequestCustomer": ".conversations", + "AttachContactToConversationRequestCustomerCustomer": ".conversations", + "AttachContactToConversationRequestCustomerIntercomUserId": ".conversations", + "AttachContactToConversationRequestCustomerUserId": ".conversations", + "AwayStatusReason": ".types", + "BadRequestError": ".errors", + "Call": ".calls", + "CallList": ".types", + "CloseConversationRequest": ".types", + "Collection": ".help_center", + "CollectionList": ".types", + "CompaniesRetrieveResponse": ".companies", + "CompaniesRetrieveResponse_Company": ".companies", + "CompaniesRetrieveResponse_List": ".companies", + "Company": ".companies", + "CompanyAttachedContacts": ".types", + "CompanyAttachedSegments": ".types", + "CompanyData": ".types", + "CompanyList": ".types", + "CompanyPlan": ".companies", + "CompanyScroll": ".types", + "CompanySegments": ".companies", + "CompanyTags": ".companies", + "Contact": ".contacts", + "ContactArchived": ".types", + "ContactAttachedCompanies": ".types", + "ContactBlocked": ".types", + "ContactCompanies": ".types", + "ContactDeleted": ".types", + "ContactList": ".types", + "ContactLocation": ".types", + "ContactNotes": ".types", + "ContactReference": ".types", + "ContactReplyBaseRequest": ".types", + "ContactReplyBaseRequestReplyOptionsItem": ".types", + "ContactReplyConversationRequest": ".types", + "ContactReplyEmailRequest": ".types", + "ContactReplyIntercomUserIdRequest": ".types", + "ContactReplyTicketEmailRequest": ".types", + "ContactReplyTicketIntercomUserIdRequest": ".types", + "ContactReplyTicketRequest": ".types", + "ContactReplyTicketUserIdRequest": ".types", + "ContactReplyUserIdRequest": ".types", + "ContactSegments": ".types", + "ContactSocialProfiles": ".types", + "ContactSubscriptionTypes": ".types", + "ContactTags": ".types", + "ContactUnarchived": ".types", + "ContactsCreateResponse": ".contacts", + "ContactsFindResponse": ".contacts", + "ContactsMergeLeadInUserResponse": ".contacts", + "ContactsUpdateResponse": ".contacts", + "ContentImportSource": ".ai_content", + "ContentImportSourceStatus": ".ai_content", + "ContentImportSourceSyncBehavior": ".ai_content", + "ContentImportSourcesList": ".ai_content", + "ContentSource": ".ai_content_source", + "ContentSourcesList": ".types", + "Conversation": ".conversations", + "ConversationAttachmentFiles": ".types", + "ConversationAttributeUpdatedByAdmin": ".types", + "ConversationAttributeUpdatedByAdminAttribute": ".types", + "ConversationAttributeUpdatedByAdminValue": ".types", + "ConversationAttributeUpdatedByWorkflow": ".types", + "ConversationAttributeUpdatedByWorkflowAttribute": ".types", + "ConversationAttributeUpdatedByWorkflowValue": ".types", + "ConversationAttributeUpdatedByWorkflowWorkflow": ".types", + "ConversationContacts": ".types", + "ConversationDeleted": ".types", + "ConversationFirstContactReply": ".types", + "ConversationList": ".types", + "ConversationPart": ".types", + "ConversationPartAuthor": ".types", + "ConversationPartMetadata": ".types", + "ConversationPartMetadataQuickReplyOptionsItem": ".types", + "ConversationPartState": ".types", + "ConversationParts": ".types", + "ConversationPriority": ".conversations", + "ConversationRating": ".types", + "ConversationResponseTime": ".types", + "ConversationSource": ".types", + "ConversationSourceType": ".types", + "ConversationState": ".conversations", + "ConversationStatistics": ".types", + "ConversationTeammates": ".types", + "ConversationsManageRequestBody": ".conversations", + "ConversationsManageRequestBody_Assignment": ".conversations", + "ConversationsManageRequestBody_Close": ".conversations", + "ConversationsManageRequestBody_Open": ".conversations", + "ConversationsManageRequestBody_Snoozed": ".conversations", + "ConvertVisitorRequestUser": ".visitors", + "ConvertVisitorRequestVisitor": ".visitors", + "CreateArticleRequest": ".types", + "CreateArticleRequestParentType": ".types", + "CreateArticleRequestState": ".types", + "CreateContactRequest": ".types", + "CreateContactRequestTwo": ".types", + "CreateContactRequestWithEmail": ".types", + "CreateContactRequestWithExternalId": ".types", + "CreateContactRequestWithRole": ".types", + "CreateContentImportSourceRequestStatus": ".ai_content", + "CreateConversationRequestFrom": ".conversations", + "CreateConversationRequestFromType": ".conversations", + "CreateDataAttributeRequest": ".types", + "CreateDataAttributeRequestOne": ".types", + "CreateDataAttributeRequestOneDataType": ".types", + "CreateDataAttributeRequestOptions": ".types", + "CreateDataAttributeRequestOptionsOptionsItem": ".types", + "CreateDataEventRequest": ".types", + "CreateDataEventRequestTwo": ".types", + "CreateDataEventRequestWithEmail": ".types", + "CreateDataEventRequestWithId": ".types", + "CreateDataEventRequestWithUserId": ".types", + "CreateDataEventSummariesRequestEventSummaries": ".events", + "CreateInternalArticleRequest": ".types", + "CreateMessageRequest": ".types", + "CreateMessageRequestFrom": ".types", + "CreateMessageRequestTo": ".types", + "CreateMessageRequestType": ".types", + "CreateMessageRequestWithEmail": ".types", + "CreateMessageRequestWithInapp": ".types", + "CreateMessageRequest_Email": ".types", + "CreateMessageRequest_Inapp": ".types", + "CreateOrUpdateCompanyRequest": ".types", + "CreateOrUpdateTagRequest": ".types", + "CreatePhoneSwitchRequest": ".types", + "CreateTicketReplyWithCommentRequest": ".types", + "CreateTicketRequestAssignment": ".types", + "CreateTicketRequestBody": ".types", + "CreateTicketRequestContactsItem": ".types", + "CreateTicketRequestContactsItemEmail": ".types", + "CreateTicketRequestContactsItemExternalId": ".types", + "CreateTicketRequestContactsItemId": ".types", + "CreateTicketTypeRequest": ".types", + "CreateTicketTypeRequestCategory": ".types", + "CursorPages": ".types", + "CustomActionFinished": ".types", + "CustomActionFinishedAction": ".types", + "CustomActionFinishedActionResult": ".types", + "CustomActionStarted": ".types", + "CustomActionStartedAction": ".types", + "CustomAttributes": ".types", + "CustomAttributesValue": ".types", + "CustomChannelAttribute": ".types", + "CustomChannelBaseEvent": ".types", + "CustomChannelContact": ".types", + "CustomChannelContactType": ".types", + "CustomChannelNotificationResponse": ".types", + "CustomObjectInstance": ".custom_object_instances", + "CustomObjectInstanceDeleted": ".types", + "CustomObjectInstanceList": ".types", + "CustomerRequest": ".types", + "CustomerRequestEmail": ".types", + "CustomerRequestIntercomUserId": ".types", + "CustomerRequestUserId": ".types", + "DataAttribute": ".data_attributes", + "DataAttributeDataType": ".data_attributes", + "DataAttributeList": ".types", + "DataAttributeModel": ".data_attributes", + "DataAttributesListRequestModel": ".data_attributes", + "DataEvent": ".data_events", + "DataEventList": ".types", + "DataEventListPages": ".types", + "DataEventSummary": ".types", + "DataEventSummaryItem": ".types", + "DataExport": ".data_export", + "DataExportCsv": ".types", + "DataExportExportReportingDataResponse": ".data_export", + "DataExportStatus": ".data_export", + "Datetime": ".types", + "DeleteTicketResponse": ".tickets", + "DeletedArticleObject": ".types", + "DeletedCollectionObject": ".types", + "DeletedCompanyObject": ".types", + "DeletedInternalArticleObject": ".types", + "DeletedObject": ".types", + "EmailAddressHeader": ".types", + "EmailMessageMetadata": ".types", + "Error": ".types", + "ErrorErrorsItem": ".types", + "EventDetails": ".types", + "ExternalPage": ".ai_content", + "ExternalPagesList": ".ai_content", + "FileAttribute": ".types", + "ForbiddenError": ".errors", + "GetExportReportingDataGetDatasetsResponse": ".export", + "GetExportReportingDataGetDatasetsResponseDataItem": ".export", + "GetExportReportingDataGetDatasetsResponseDataItemAttributesItem": ".export", + "GroupContent": ".types", + "GroupTranslatedContent": ".types", + "HelpCenter": ".help_center", + "HelpCenterList": ".help_center", + "Intercom": ".client", + "IntercomEnvironment": ".environment", + "InternalArticle": ".articles", + "InternalArticleList": ".types", + "InternalArticleListItem": ".internal_articles", + "InternalArticleSearchResponse": ".internal_articles", + "InternalArticleSearchResponseData": ".internal_articles", + "Jobs": ".jobs", + "JobsStatus": ".jobs", + "LinkedObject": ".types", + "LinkedObjectCategory": ".types", + "LinkedObjectList": ".types", + "LinkedObjectType": ".types", + "ListCallsWithTranscriptsResponse": ".calls", + "ListCallsWithTranscriptsResponseDataItem": ".calls", + "Message": ".messages", + "MessageMessageType": ".messages", + "Metadata": ".types", + "MultipleFilterSearchRequest": ".types", + "MultipleFilterSearchRequestOperator": ".types", + "MultipleFilterSearchRequestValue": ".types", + "NewsItem": ".news", + "NewsItemRequest": ".types", + "NewsItemRequestState": ".types", + "NewsItemState": ".news", + "Newsfeed": ".news", + "NewsfeedAssignment": ".news", + "NotFoundError": ".errors", + "NotFoundErrorBody": ".types", + "NotFoundErrorBodyErrorsItem": ".types", + "Note": ".notes", + "NoteContact": ".notes", + "NoteList": ".types", + "OffsetPages": ".types", + "OpenConversationRequest": ".types", + "OperatorWorkflowEvent": ".types", + "OperatorWorkflowEventEvent": ".types", + "OperatorWorkflowEventWorkflow": ".types", + "PagesLink": ".types", + "PaginatedResponse": ".types", + "PaginatedResponseDataItem": ".types", + "PaginatedResponseDataItem_NewsItem": ".types", + "PaginatedResponseDataItem_Newsfeed": ".types", + "PaginatedResponseType": ".types", + "PartAttachment": ".types", + "PhoneSwitch": ".types", + "PostExportReportingDataEnqueueResponse": ".export", + "QuickReplyOption": ".types", + "Recipient": ".types", + "RecipientType": ".types", + "RedactConversationRequest": ".types", + "RedactConversationRequestConversationPart": ".types", + "RedactConversationRequestSource": ".types", + "RedactConversationRequest_ConversationPart": ".types", + "RedactConversationRequest_Source": ".types", + "Reference": ".types", + "ReplyConversationRequest": ".types", + "SearchRequest": ".types", + "SearchRequestQuery": ".types", + "Segment": ".segments", + "SegmentList": ".types", + "SegmentPersonType": ".segments", + "ShowContactByExternalIdResponse": ".contacts", + "SingleFilterSearchRequest": ".types", + "SingleFilterSearchRequestOperator": ".types", + "SingleFilterSearchRequestValue": ".types", + "SingleFilterSearchRequestValueTwoItem": ".types", + "SlaApplied": ".types", + "SlaAppliedSlaStatus": ".types", + "SnoozeConversationRequest": ".types", + "SocialProfile": ".types", + "StartingAfterPaging": ".types", + "SubscriptionType": ".subscription_types", + "SubscriptionTypeConsentType": ".subscription_types", + "SubscriptionTypeContentTypesItem": ".subscription_types", + "SubscriptionTypeList": ".types", + "SubscriptionTypeState": ".subscription_types", + "Tag": ".tags", + "TagBasic": ".tags", + "TagCompanyRequest": ".types", + "TagCompanyRequestCompaniesItem": ".types", + "TagList": ".types", + "TagMultipleUsersRequest": ".types", + "TagMultipleUsersRequestUsersItem": ".types", + "Tags": ".types", + "TagsCreateRequestBody": ".tags", + "Team": ".teams", + "TeamList": ".types", + "TeamPriorityLevel": ".types", + "Ticket": ".tickets", + "TicketCategory": ".tickets", + "TicketContacts": ".tickets", + "TicketCustomAttributes": ".types", + "TicketList": ".types", + "TicketPart": ".tickets", + "TicketPartAuthor": ".types", + "TicketPartAuthorType": ".types", + "TicketPartPreviousTicketState": ".tickets", + "TicketPartTicketState": ".tickets", + "TicketPartUpdatedAttributeData": ".tickets", + "TicketPartUpdatedAttributeDataAttribute": ".tickets", + "TicketPartUpdatedAttributeDataValue": ".tickets", + "TicketPartUpdatedAttributeDataValueId": ".tickets", + "TicketPartUpdatedAttributeDataValueLabel": ".tickets", + "TicketParts": ".types", + "TicketReply": ".types", + "TicketReplyPartType": ".types", + "TicketRequestCustomAttributes": ".types", + "TicketState": ".tickets", + "TicketStateCategory": ".tickets", + "TicketStateDetailed": ".tickets", + "TicketStateDetailedCategory": ".tickets", + "TicketStateDetailedTicketTypes": ".tickets", + "TicketStateList": ".types", + "TicketType": ".tickets", + "TicketTypeAttribute": ".types", + "TicketTypeAttributeDataType": ".types", + "TicketTypeAttributeList": ".types", + "TicketTypeCategory": ".tickets", + "TicketTypeList": ".types", + "TicketTypeTicketStates": ".tickets", + "TicketsReplyRequestBody": ".tickets", + "TooManyRequestsError": ".errors", + "Translation": ".types", + "UnauthorizedError": ".errors", + "UnprocessableEntityError": ".errors", + "UntagCompanyRequest": ".types", + "UntagCompanyRequestCompaniesItem": ".types", + "UpdateArticleRequestBody": ".types", + "UpdateArticleRequestBodyParentType": ".types", + "UpdateArticleRequestState": ".articles", + "UpdateCompanyRequestBody": ".types", + "UpdateContentImportSourceRequestStatus": ".ai_content", + "UpdateContentImportSourceRequestSyncBehavior": ".ai_content", + "UpdateDataAttributeRequestBody": ".types", + "UpdateDataAttributeRequestOptions": ".types", + "UpdateDataAttributeRequestOptionsOptionsItem": ".types", + "UpdateTicketTypeRequestCategory": ".ticket_types", + "UpdateVisitorRequest": ".types", + "UpdateVisitorRequestOne": ".types", + "UpdateVisitorRequestWithId": ".types", + "UpdateVisitorRequestWithUserId": ".types", + "UserWithId": ".visitors", + "UserWithUserId": ".visitors", + "Visitor": ".types", + "VisitorAvatar": ".types", + "VisitorCompanies": ".types", + "VisitorDeletedObject": ".types", + "VisitorLocationData": ".types", + "VisitorSegments": ".types", + "VisitorSocialProfiles": ".types", + "VisitorTags": ".types", + "VisitorTagsTagsItem": ".types", + "VisitorWithEmail": ".visitors", + "VisitorWithId": ".visitors", + "VisitorWithUserId": ".visitors", + "WhatsappMessageStatusList": ".types", + "WhatsappMessageStatusListEventsItem": ".types", + "WhatsappMessageStatusListEventsItemStatus": ".types", + "WhatsappMessageStatusListPages": ".types", + "WhatsappMessageStatusListPagesNext": ".types", + "__version__": ".version", + "admins": ".admins", + "ai_agent": ".ai_agent", + "ai_content": ".ai_content", + "ai_content_source": ".ai_content_source", + "articles": ".articles", + "away_status_reasons": ".away_status_reasons", + "calls": ".calls", + "companies": ".companies", + "contacts": ".contacts", + "conversations": ".conversations", + "custom_channel_events": ".custom_channel_events", + "custom_object_instances": ".custom_object_instances", + "data_attributes": ".data_attributes", + "data_events": ".data_events", + "data_export": ".data_export", + "events": ".events", + "export": ".export", + "help_center": ".help_center", + "help_centers": ".help_centers", + "internal_articles": ".internal_articles", + "jobs": ".jobs", + "messages": ".messages", + "news": ".news", + "notes": ".notes", + "phone_call_redirects": ".phone_call_redirects", + "segments": ".segments", + "subscription_types": ".subscription_types", + "tags": ".tags", + "teams": ".teams", + "ticket_states": ".ticket_states", + "ticket_types": ".ticket_types", + "tickets": ".tickets", + "unstable": ".unstable", + "visitors": ".visitors", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "ActivityLog", + "ActivityLogActivityType", + "ActivityLogList", + "ActivityLogMetadata", + "ActivityLogMetadataTeam", + "ActivityLogPerformedBy", + "AddressableList", + "Admin", + "AdminAvatar", + "AdminList", + "AdminPriorityLevel", + "AdminReplyConversationRequest", + "AdminReplyConversationRequestMessageType", + "AdminReplyTicketRequest", + "AdminReplyTicketRequestMessageType", + "AdminReplyTicketRequestReplyOptionsItem", + "AdminWithApp", + "AdminWithAppAvatar", + "AiAgent", + "AiAgentLastAnswerType", + "AiAgentResolutionState", + "AiAgentSourceType", + "App", + "Article", + "ArticleContent", + "ArticleContentState", + "ArticleList", + "ArticleListItem", + "ArticleListItemState", + "ArticleSearchHighlights", + "ArticleSearchHighlightsHighlightedSummaryItemItem", + "ArticleSearchHighlightsHighlightedSummaryItemItemType", + "ArticleSearchHighlightsHighlightedTitleItem", + "ArticleSearchHighlightsHighlightedTitleItemType", + "ArticleSearchResponse", + "ArticleSearchResponseData", + "ArticleStatistics", + "ArticleTranslatedContent", + "AssignConversationRequest", + "AssignConversationRequestType", + "AsyncIntercom", + "AttachContactToConversationRequestCustomer", + "AttachContactToConversationRequestCustomerCustomer", + "AttachContactToConversationRequestCustomerIntercomUserId", + "AttachContactToConversationRequestCustomerUserId", + "AwayStatusReason", + "BadRequestError", + "Call", + "CallList", + "CloseConversationRequest", + "Collection", + "CollectionList", + "CompaniesRetrieveResponse", + "CompaniesRetrieveResponse_Company", + "CompaniesRetrieveResponse_List", + "Company", + "CompanyAttachedContacts", + "CompanyAttachedSegments", + "CompanyData", + "CompanyList", + "CompanyPlan", + "CompanyScroll", + "CompanySegments", + "CompanyTags", + "Contact", + "ContactArchived", + "ContactAttachedCompanies", + "ContactBlocked", + "ContactCompanies", + "ContactDeleted", + "ContactList", + "ContactLocation", + "ContactNotes", + "ContactReference", + "ContactReplyBaseRequest", + "ContactReplyBaseRequestReplyOptionsItem", + "ContactReplyConversationRequest", + "ContactReplyEmailRequest", + "ContactReplyIntercomUserIdRequest", + "ContactReplyTicketEmailRequest", + "ContactReplyTicketIntercomUserIdRequest", + "ContactReplyTicketRequest", + "ContactReplyTicketUserIdRequest", + "ContactReplyUserIdRequest", + "ContactSegments", + "ContactSocialProfiles", + "ContactSubscriptionTypes", + "ContactTags", + "ContactUnarchived", + "ContactsCreateResponse", + "ContactsFindResponse", + "ContactsMergeLeadInUserResponse", + "ContactsUpdateResponse", + "ContentImportSource", + "ContentImportSourceStatus", + "ContentImportSourceSyncBehavior", + "ContentImportSourcesList", + "ContentSource", + "ContentSourcesList", + "Conversation", + "ConversationAttachmentFiles", + "ConversationAttributeUpdatedByAdmin", + "ConversationAttributeUpdatedByAdminAttribute", + "ConversationAttributeUpdatedByAdminValue", + "ConversationAttributeUpdatedByWorkflow", + "ConversationAttributeUpdatedByWorkflowAttribute", + "ConversationAttributeUpdatedByWorkflowValue", + "ConversationAttributeUpdatedByWorkflowWorkflow", + "ConversationContacts", + "ConversationDeleted", + "ConversationFirstContactReply", + "ConversationList", + "ConversationPart", + "ConversationPartAuthor", + "ConversationPartMetadata", + "ConversationPartMetadataQuickReplyOptionsItem", + "ConversationPartState", + "ConversationParts", + "ConversationPriority", + "ConversationRating", + "ConversationResponseTime", + "ConversationSource", + "ConversationSourceType", + "ConversationState", + "ConversationStatistics", + "ConversationTeammates", + "ConversationsManageRequestBody", + "ConversationsManageRequestBody_Assignment", + "ConversationsManageRequestBody_Close", + "ConversationsManageRequestBody_Open", + "ConversationsManageRequestBody_Snoozed", + "ConvertVisitorRequestUser", + "ConvertVisitorRequestVisitor", + "CreateArticleRequest", + "CreateArticleRequestParentType", + "CreateArticleRequestState", + "CreateContactRequest", + "CreateContactRequestTwo", + "CreateContactRequestWithEmail", + "CreateContactRequestWithExternalId", + "CreateContactRequestWithRole", + "CreateContentImportSourceRequestStatus", + "CreateConversationRequestFrom", + "CreateConversationRequestFromType", + "CreateDataAttributeRequest", + "CreateDataAttributeRequestOne", + "CreateDataAttributeRequestOneDataType", + "CreateDataAttributeRequestOptions", + "CreateDataAttributeRequestOptionsOptionsItem", + "CreateDataEventRequest", + "CreateDataEventRequestTwo", + "CreateDataEventRequestWithEmail", + "CreateDataEventRequestWithId", + "CreateDataEventRequestWithUserId", + "CreateDataEventSummariesRequestEventSummaries", + "CreateInternalArticleRequest", + "CreateMessageRequest", + "CreateMessageRequestFrom", + "CreateMessageRequestTo", + "CreateMessageRequestType", + "CreateMessageRequestWithEmail", + "CreateMessageRequestWithInapp", + "CreateMessageRequest_Email", + "CreateMessageRequest_Inapp", + "CreateOrUpdateCompanyRequest", + "CreateOrUpdateTagRequest", + "CreatePhoneSwitchRequest", + "CreateTicketReplyWithCommentRequest", + "CreateTicketRequestAssignment", + "CreateTicketRequestBody", + "CreateTicketRequestContactsItem", + "CreateTicketRequestContactsItemEmail", + "CreateTicketRequestContactsItemExternalId", + "CreateTicketRequestContactsItemId", + "CreateTicketTypeRequest", + "CreateTicketTypeRequestCategory", + "CursorPages", + "CustomActionFinished", + "CustomActionFinishedAction", + "CustomActionFinishedActionResult", + "CustomActionStarted", + "CustomActionStartedAction", + "CustomAttributes", + "CustomAttributesValue", + "CustomChannelAttribute", + "CustomChannelBaseEvent", + "CustomChannelContact", + "CustomChannelContactType", + "CustomChannelNotificationResponse", + "CustomObjectInstance", + "CustomObjectInstanceDeleted", + "CustomObjectInstanceList", + "CustomerRequest", + "CustomerRequestEmail", + "CustomerRequestIntercomUserId", + "CustomerRequestUserId", + "DataAttribute", + "DataAttributeDataType", + "DataAttributeList", + "DataAttributeModel", + "DataAttributesListRequestModel", + "DataEvent", + "DataEventList", + "DataEventListPages", + "DataEventSummary", + "DataEventSummaryItem", + "DataExport", + "DataExportCsv", + "DataExportExportReportingDataResponse", + "DataExportStatus", + "Datetime", + "DeleteTicketResponse", + "DeletedArticleObject", + "DeletedCollectionObject", + "DeletedCompanyObject", + "DeletedInternalArticleObject", + "DeletedObject", + "EmailAddressHeader", + "EmailMessageMetadata", + "Error", + "ErrorErrorsItem", + "EventDetails", + "ExternalPage", + "ExternalPagesList", + "FileAttribute", + "ForbiddenError", + "GetExportReportingDataGetDatasetsResponse", + "GetExportReportingDataGetDatasetsResponseDataItem", + "GetExportReportingDataGetDatasetsResponseDataItemAttributesItem", + "GroupContent", + "GroupTranslatedContent", + "HelpCenter", + "HelpCenterList", + "Intercom", + "IntercomEnvironment", + "InternalArticle", + "InternalArticleList", + "InternalArticleListItem", + "InternalArticleSearchResponse", + "InternalArticleSearchResponseData", + "Jobs", + "JobsStatus", + "LinkedObject", + "LinkedObjectCategory", + "LinkedObjectList", + "LinkedObjectType", + "ListCallsWithTranscriptsResponse", + "ListCallsWithTranscriptsResponseDataItem", + "Message", + "MessageMessageType", + "Metadata", + "MultipleFilterSearchRequest", + "MultipleFilterSearchRequestOperator", + "MultipleFilterSearchRequestValue", + "NewsItem", + "NewsItemRequest", + "NewsItemRequestState", + "NewsItemState", + "Newsfeed", + "NewsfeedAssignment", + "NotFoundError", + "NotFoundErrorBody", + "NotFoundErrorBodyErrorsItem", + "Note", + "NoteContact", + "NoteList", + "OffsetPages", + "OpenConversationRequest", + "OperatorWorkflowEvent", + "OperatorWorkflowEventEvent", + "OperatorWorkflowEventWorkflow", + "PagesLink", + "PaginatedResponse", + "PaginatedResponseDataItem", + "PaginatedResponseDataItem_NewsItem", + "PaginatedResponseDataItem_Newsfeed", + "PaginatedResponseType", + "PartAttachment", + "PhoneSwitch", + "PostExportReportingDataEnqueueResponse", + "QuickReplyOption", + "Recipient", + "RecipientType", + "RedactConversationRequest", + "RedactConversationRequestConversationPart", + "RedactConversationRequestSource", + "RedactConversationRequest_ConversationPart", + "RedactConversationRequest_Source", + "Reference", + "ReplyConversationRequest", + "SearchRequest", + "SearchRequestQuery", + "Segment", + "SegmentList", + "SegmentPersonType", + "ShowContactByExternalIdResponse", + "SingleFilterSearchRequest", + "SingleFilterSearchRequestOperator", + "SingleFilterSearchRequestValue", + "SingleFilterSearchRequestValueTwoItem", + "SlaApplied", + "SlaAppliedSlaStatus", + "SnoozeConversationRequest", + "SocialProfile", + "StartingAfterPaging", + "SubscriptionType", + "SubscriptionTypeConsentType", + "SubscriptionTypeContentTypesItem", + "SubscriptionTypeList", + "SubscriptionTypeState", + "Tag", + "TagBasic", + "TagCompanyRequest", + "TagCompanyRequestCompaniesItem", + "TagList", + "TagMultipleUsersRequest", + "TagMultipleUsersRequestUsersItem", + "Tags", + "TagsCreateRequestBody", + "Team", + "TeamList", + "TeamPriorityLevel", + "Ticket", + "TicketCategory", + "TicketContacts", + "TicketCustomAttributes", + "TicketList", + "TicketPart", + "TicketPartAuthor", + "TicketPartAuthorType", + "TicketPartPreviousTicketState", + "TicketPartTicketState", + "TicketPartUpdatedAttributeData", + "TicketPartUpdatedAttributeDataAttribute", + "TicketPartUpdatedAttributeDataValue", + "TicketPartUpdatedAttributeDataValueId", + "TicketPartUpdatedAttributeDataValueLabel", + "TicketParts", + "TicketReply", + "TicketReplyPartType", + "TicketRequestCustomAttributes", + "TicketState", + "TicketStateCategory", + "TicketStateDetailed", + "TicketStateDetailedCategory", + "TicketStateDetailedTicketTypes", + "TicketStateList", + "TicketType", + "TicketTypeAttribute", + "TicketTypeAttributeDataType", + "TicketTypeAttributeList", + "TicketTypeCategory", + "TicketTypeList", + "TicketTypeTicketStates", + "TicketsReplyRequestBody", + "TooManyRequestsError", + "Translation", + "UnauthorizedError", + "UnprocessableEntityError", + "UntagCompanyRequest", + "UntagCompanyRequestCompaniesItem", + "UpdateArticleRequestBody", + "UpdateArticleRequestBodyParentType", + "UpdateArticleRequestState", + "UpdateCompanyRequestBody", + "UpdateContentImportSourceRequestStatus", + "UpdateContentImportSourceRequestSyncBehavior", + "UpdateDataAttributeRequestBody", + "UpdateDataAttributeRequestOptions", + "UpdateDataAttributeRequestOptionsOptionsItem", + "UpdateTicketTypeRequestCategory", + "UpdateVisitorRequest", + "UpdateVisitorRequestOne", + "UpdateVisitorRequestWithId", + "UpdateVisitorRequestWithUserId", + "UserWithId", + "UserWithUserId", + "Visitor", + "VisitorAvatar", + "VisitorCompanies", + "VisitorDeletedObject", + "VisitorLocationData", + "VisitorSegments", + "VisitorSocialProfiles", + "VisitorTags", + "VisitorTagsTagsItem", + "VisitorWithEmail", + "VisitorWithId", + "VisitorWithUserId", + "WhatsappMessageStatusList", + "WhatsappMessageStatusListEventsItem", + "WhatsappMessageStatusListEventsItemStatus", + "WhatsappMessageStatusListPages", + "WhatsappMessageStatusListPagesNext", + "__version__", + "admins", + "ai_agent", + "ai_content", + "ai_content_source", + "articles", + "away_status_reasons", + "calls", + "companies", + "contacts", + "conversations", + "custom_channel_events", + "custom_object_instances", + "data_attributes", + "data_events", + "data_export", + "events", + "export", + "help_center", + "help_centers", + "internal_articles", + "jobs", + "messages", + "news", + "notes", + "phone_call_redirects", + "segments", + "subscription_types", + "tags", + "teams", + "ticket_states", + "ticket_types", + "tickets", + "unstable", + "visitors", +] diff --git a/src/intercom/admins/__init__.py b/src/intercom/admins/__init__.py new file mode 100644 index 00000000..443d42eb --- /dev/null +++ b/src/intercom/admins/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import Admin, AdminAvatar +_dynamic_imports: typing.Dict[str, str] = {"Admin": ".types", "AdminAvatar": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Admin", "AdminAvatar"] diff --git a/src/intercom/admins/client.py b/src/intercom/admins/client.py new file mode 100644 index 00000000..645c32e4 --- /dev/null +++ b/src/intercom/admins/client.py @@ -0,0 +1,466 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.activity_log_list import ActivityLogList +from ..types.admin_list import AdminList +from ..types.admin_with_app import AdminWithApp +from .raw_client import AsyncRawAdminsClient, RawAdminsClient +from .types.admin import Admin + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class AdminsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawAdminsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawAdminsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawAdminsClient + """ + return self._raw_client + + def identify(self, *, request_options: typing.Optional[RequestOptions] = None) -> typing.Optional[AdminWithApp]: + """ + + You can view the currently authorised admin along with the embedded app object (a "workspace" in legacy terminology). + + > 🚧 Single Sign On + > + > If you are building a custom "Log in with Intercom" flow for your site, and you call the `/me` endpoint to identify the logged-in user, you should not accept any sign-ins from users with unverified email addresses as it poses a potential impersonation security risk. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[AdminWithApp] + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.admins.identify() + """ + _response = self._raw_client.identify(request_options=request_options) + return _response.data + + def away( + self, + admin_id: int, + *, + away_mode_enabled: bool, + away_mode_reassign: bool, + away_status_reason_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[Admin]: + """ + You can set an Admin as away for the Inbox. + + Parameters + ---------- + admin_id : int + The unique identifier of a given admin + + away_mode_enabled : bool + Set to "true" to change the status of the admin to away. + + away_mode_reassign : bool + Set to "true" to assign any new conversation replies to your default inbox. + + away_status_reason_id : typing.Optional[int] + The unique identifier of the away status reason + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Admin] + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.admins.away( + admin_id=1, + away_mode_enabled=True, + away_mode_reassign=True, + away_status_reason_id=12345, + ) + """ + _response = self._raw_client.away( + admin_id, + away_mode_enabled=away_mode_enabled, + away_mode_reassign=away_mode_reassign, + away_status_reason_id=away_status_reason_id, + request_options=request_options, + ) + return _response.data + + def list_all_activity_logs( + self, + *, + created_at_after: str, + created_at_before: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ActivityLogList: + """ + You can get a log of activities by all admins in an app. + + Parameters + ---------- + created_at_after : str + The start date that you request data for. It must be formatted as a UNIX timestamp. + + created_at_before : typing.Optional[str] + The end date that you request data for. It must be formatted as a UNIX timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ActivityLogList + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.admins.list_all_activity_logs( + created_at_after="1677253093", + created_at_before="1677861493", + ) + """ + _response = self._raw_client.list_all_activity_logs( + created_at_after=created_at_after, created_at_before=created_at_before, request_options=request_options + ) + return _response.data + + def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> AdminList: + """ + You can fetch a list of admins for a given workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AdminList + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.admins.list() + """ + _response = self._raw_client.list(request_options=request_options) + return _response.data + + def find(self, admin_id: int, *, request_options: typing.Optional[RequestOptions] = None) -> typing.Optional[Admin]: + """ + You can retrieve the details of a single admin. + + Parameters + ---------- + admin_id : int + The unique identifier of a given admin + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Admin] + Admin found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.admins.find( + admin_id=1, + ) + """ + _response = self._raw_client.find(admin_id, request_options=request_options) + return _response.data + + +class AsyncAdminsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawAdminsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawAdminsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawAdminsClient + """ + return self._raw_client + + async def identify( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[AdminWithApp]: + """ + + You can view the currently authorised admin along with the embedded app object (a "workspace" in legacy terminology). + + > 🚧 Single Sign On + > + > If you are building a custom "Log in with Intercom" flow for your site, and you call the `/me` endpoint to identify the logged-in user, you should not accept any sign-ins from users with unverified email addresses as it poses a potential impersonation security risk. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[AdminWithApp] + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.admins.identify() + + + asyncio.run(main()) + """ + _response = await self._raw_client.identify(request_options=request_options) + return _response.data + + async def away( + self, + admin_id: int, + *, + away_mode_enabled: bool, + away_mode_reassign: bool, + away_status_reason_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[Admin]: + """ + You can set an Admin as away for the Inbox. + + Parameters + ---------- + admin_id : int + The unique identifier of a given admin + + away_mode_enabled : bool + Set to "true" to change the status of the admin to away. + + away_mode_reassign : bool + Set to "true" to assign any new conversation replies to your default inbox. + + away_status_reason_id : typing.Optional[int] + The unique identifier of the away status reason + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Admin] + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.admins.away( + admin_id=1, + away_mode_enabled=True, + away_mode_reassign=True, + away_status_reason_id=12345, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.away( + admin_id, + away_mode_enabled=away_mode_enabled, + away_mode_reassign=away_mode_reassign, + away_status_reason_id=away_status_reason_id, + request_options=request_options, + ) + return _response.data + + async def list_all_activity_logs( + self, + *, + created_at_after: str, + created_at_before: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ActivityLogList: + """ + You can get a log of activities by all admins in an app. + + Parameters + ---------- + created_at_after : str + The start date that you request data for. It must be formatted as a UNIX timestamp. + + created_at_before : typing.Optional[str] + The end date that you request data for. It must be formatted as a UNIX timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ActivityLogList + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.admins.list_all_activity_logs( + created_at_after="1677253093", + created_at_before="1677861493", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_all_activity_logs( + created_at_after=created_at_after, created_at_before=created_at_before, request_options=request_options + ) + return _response.data + + async def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> AdminList: + """ + You can fetch a list of admins for a given workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AdminList + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.admins.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list(request_options=request_options) + return _response.data + + async def find( + self, admin_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[Admin]: + """ + You can retrieve the details of a single admin. + + Parameters + ---------- + admin_id : int + The unique identifier of a given admin + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Admin] + Admin found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.admins.find( + admin_id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.find(admin_id, request_options=request_options) + return _response.data diff --git a/src/intercom/admins/raw_client.py b/src/intercom/admins/raw_client.py new file mode 100644 index 00000000..ea8a9c14 --- /dev/null +++ b/src/intercom/admins/raw_client.py @@ -0,0 +1,652 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ..core.unchecked_base_model import construct_type +from ..errors.bad_request_error import BadRequestError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.activity_log_list import ActivityLogList +from ..types.admin_list import AdminList +from ..types.admin_with_app import AdminWithApp +from ..types.error import Error +from .types.admin import Admin + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawAdminsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def identify( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[typing.Optional[AdminWithApp]]: + """ + + You can view the currently authorised admin along with the embedded app object (a "workspace" in legacy terminology). + + > 🚧 Single Sign On + > + > If you are building a custom "Log in with Intercom" flow for your site, and you call the `/me` endpoint to identify the logged-in user, you should not accept any sign-ins from users with unverified email addresses as it poses a potential impersonation security risk. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[AdminWithApp]] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "me", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[AdminWithApp], + construct_type( + type_=typing.Optional[AdminWithApp], # 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 away( + self, + admin_id: int, + *, + away_mode_enabled: bool, + away_mode_reassign: bool, + away_status_reason_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[typing.Optional[Admin]]: + """ + You can set an Admin as away for the Inbox. + + Parameters + ---------- + admin_id : int + The unique identifier of a given admin + + away_mode_enabled : bool + Set to "true" to change the status of the admin to away. + + away_mode_reassign : bool + Set to "true" to assign any new conversation replies to your default inbox. + + away_status_reason_id : typing.Optional[int] + The unique identifier of the away status reason + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[Admin]] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + f"admins/{jsonable_encoder(admin_id)}/away", + method="PUT", + json={ + "away_mode_enabled": away_mode_enabled, + "away_mode_reassign": away_mode_reassign, + "away_status_reason_id": away_status_reason_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Admin], + construct_type( + type_=typing.Optional[Admin], # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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_all_activity_logs( + self, + *, + created_at_after: str, + created_at_before: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ActivityLogList]: + """ + You can get a log of activities by all admins in an app. + + Parameters + ---------- + created_at_after : str + The start date that you request data for. It must be formatted as a UNIX timestamp. + + created_at_before : typing.Optional[str] + The end date that you request data for. It must be formatted as a UNIX timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ActivityLogList] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "admins/activity_logs", + method="GET", + params={ + "created_at_after": created_at_after, + "created_at_before": created_at_before, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ActivityLogList, + construct_type( + type_=ActivityLogList, # 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 list(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[AdminList]: + """ + You can fetch a list of admins for a given workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[AdminList] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "admins", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AdminList, + construct_type( + type_=AdminList, # 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 find( + self, admin_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[typing.Optional[Admin]]: + """ + You can retrieve the details of a single admin. + + Parameters + ---------- + admin_id : int + The unique identifier of a given admin + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[Admin]] + Admin found + """ + _response = self._client_wrapper.httpx_client.request( + f"admins/{jsonable_encoder(admin_id)}", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Admin], + construct_type( + type_=typing.Optional[Admin], # 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) + + +class AsyncRawAdminsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def identify( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[typing.Optional[AdminWithApp]]: + """ + + You can view the currently authorised admin along with the embedded app object (a "workspace" in legacy terminology). + + > 🚧 Single Sign On + > + > If you are building a custom "Log in with Intercom" flow for your site, and you call the `/me` endpoint to identify the logged-in user, you should not accept any sign-ins from users with unverified email addresses as it poses a potential impersonation security risk. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[AdminWithApp]] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "me", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[AdminWithApp], + construct_type( + type_=typing.Optional[AdminWithApp], # 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 away( + self, + admin_id: int, + *, + away_mode_enabled: bool, + away_mode_reassign: bool, + away_status_reason_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[typing.Optional[Admin]]: + """ + You can set an Admin as away for the Inbox. + + Parameters + ---------- + admin_id : int + The unique identifier of a given admin + + away_mode_enabled : bool + Set to "true" to change the status of the admin to away. + + away_mode_reassign : bool + Set to "true" to assign any new conversation replies to your default inbox. + + away_status_reason_id : typing.Optional[int] + The unique identifier of the away status reason + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[Admin]] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + f"admins/{jsonable_encoder(admin_id)}/away", + method="PUT", + json={ + "away_mode_enabled": away_mode_enabled, + "away_mode_reassign": away_mode_reassign, + "away_status_reason_id": away_status_reason_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Admin], + construct_type( + type_=typing.Optional[Admin], # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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_all_activity_logs( + self, + *, + created_at_after: str, + created_at_before: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ActivityLogList]: + """ + You can get a log of activities by all admins in an app. + + Parameters + ---------- + created_at_after : str + The start date that you request data for. It must be formatted as a UNIX timestamp. + + created_at_before : typing.Optional[str] + The end date that you request data for. It must be formatted as a UNIX timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ActivityLogList] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "admins/activity_logs", + method="GET", + params={ + "created_at_after": created_at_after, + "created_at_before": created_at_before, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ActivityLogList, + construct_type( + type_=ActivityLogList, # 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 list(self, *, request_options: typing.Optional[RequestOptions] = None) -> AsyncHttpResponse[AdminList]: + """ + You can fetch a list of admins for a given workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[AdminList] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "admins", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AdminList, + construct_type( + type_=AdminList, # 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 find( + self, admin_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[typing.Optional[Admin]]: + """ + You can retrieve the details of a single admin. + + Parameters + ---------- + admin_id : int + The unique identifier of a given admin + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[Admin]] + Admin found + """ + _response = await self._client_wrapper.httpx_client.request( + f"admins/{jsonable_encoder(admin_id)}", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Admin], + construct_type( + type_=typing.Optional[Admin], # 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) diff --git a/src/intercom/admins/types/__init__.py b/src/intercom/admins/types/__init__.py new file mode 100644 index 00000000..d280e048 --- /dev/null +++ b/src/intercom/admins/types/__init__.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .admin import Admin + from .admin_avatar import AdminAvatar +_dynamic_imports: typing.Dict[str, str] = {"Admin": ".admin", "AdminAvatar": ".admin_avatar"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Admin", "AdminAvatar"] diff --git a/src/intercom/admins/types/admin.py b/src/intercom/admins/types/admin.py new file mode 100644 index 00000000..b7919636 --- /dev/null +++ b/src/intercom/admins/types/admin.py @@ -0,0 +1,81 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.team_priority_level import TeamPriorityLevel +from .admin_avatar import AdminAvatar + + +class Admin(UncheckedBaseModel): + """ + Admins are teammate accounts that have access to a workspace. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `admin`. + """ + + id: str = pydantic.Field() + """ + The id representing the admin. + """ + + name: str = pydantic.Field() + """ + The name of the admin. + """ + + email: str = pydantic.Field() + """ + The email of the admin. + """ + + job_title: typing.Optional[str] = pydantic.Field(default=None) + """ + The job title of the admin. + """ + + away_mode_enabled: bool = pydantic.Field() + """ + Identifies if this admin is currently set in away mode. + """ + + away_mode_reassign: bool = pydantic.Field() + """ + Identifies if this admin is set to automatically reassign new conversations to the apps default inbox. + """ + + away_status_reason_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The unique identifier of the away status reason + """ + + has_inbox_seat: bool = pydantic.Field() + """ + Identifies if this admin has a paid inbox seat to restrict/allow features that require them. + """ + + team_ids: typing.List[int] = pydantic.Field() + """ + This object represents the avatar associated with the admin. + """ + + avatar: typing.Optional[AdminAvatar] = pydantic.Field(default=None) + """ + The avatar object associated with the admin + """ + + team_priority_level: typing.Optional[TeamPriorityLevel] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/admins/types/admin_avatar.py b/src/intercom/admins/types/admin_avatar.py new file mode 100644 index 00000000..547330c7 --- /dev/null +++ b/src/intercom/admins/types/admin_avatar.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class AdminAvatar(UncheckedBaseModel): + """ + The avatar object associated with the admin + """ + + image_url: str = pydantic.Field() + """ + URL of the admin's avatar image + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/ai_agent/__init__.py b/src/intercom/ai_agent/__init__.py new file mode 100644 index 00000000..f338d84d --- /dev/null +++ b/src/intercom/ai_agent/__init__.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import AiAgent, AiAgentLastAnswerType, AiAgentResolutionState, AiAgentSourceType +_dynamic_imports: typing.Dict[str, str] = { + "AiAgent": ".types", + "AiAgentLastAnswerType": ".types", + "AiAgentResolutionState": ".types", + "AiAgentSourceType": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["AiAgent", "AiAgentLastAnswerType", "AiAgentResolutionState", "AiAgentSourceType"] diff --git a/src/intercom/ai_agent/types/__init__.py b/src/intercom/ai_agent/types/__init__.py new file mode 100644 index 00000000..e869e949 --- /dev/null +++ b/src/intercom/ai_agent/types/__init__.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .ai_agent import AiAgent + from .ai_agent_last_answer_type import AiAgentLastAnswerType + from .ai_agent_resolution_state import AiAgentResolutionState + from .ai_agent_source_type import AiAgentSourceType +_dynamic_imports: typing.Dict[str, str] = { + "AiAgent": ".ai_agent", + "AiAgentLastAnswerType": ".ai_agent_last_answer_type", + "AiAgentResolutionState": ".ai_agent_resolution_state", + "AiAgentSourceType": ".ai_agent_source_type", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["AiAgent", "AiAgentLastAnswerType", "AiAgentResolutionState", "AiAgentSourceType"] diff --git a/src/intercom/ai_agent/types/ai_agent.py b/src/intercom/ai_agent/types/ai_agent.py new file mode 100644 index 00000000..784aaefa --- /dev/null +++ b/src/intercom/ai_agent/types/ai_agent.py @@ -0,0 +1,68 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.content_sources_list import ContentSourcesList +from .ai_agent_last_answer_type import AiAgentLastAnswerType +from .ai_agent_resolution_state import AiAgentResolutionState +from .ai_agent_source_type import AiAgentSourceType + + +class AiAgent(UncheckedBaseModel): + """ + Data related to AI Agent involvement in the conversation. + """ + + source_type: typing.Optional[AiAgentSourceType] = pydantic.Field(default=None) + """ + The type of the source that triggered AI Agent involvement in the conversation. + """ + + source_title: typing.Optional[str] = pydantic.Field(default=None) + """ + The title of the source that triggered AI Agent involvement in the conversation. If this is `essentials_plan_setup` then it will return `null`. + """ + + last_answer_type: typing.Optional[AiAgentLastAnswerType] = pydantic.Field(default=None) + """ + The type of the last answer delivered by AI Agent. If no answer was delivered then this will return `null` + """ + + resolution_state: typing.Optional[AiAgentResolutionState] = pydantic.Field(default=None) + """ + The resolution state of AI Agent. If no AI or custom answer has been delivered then this will return `null`. + """ + + rating: typing.Optional[int] = pydantic.Field(default=None) + """ + The customer satisfaction rating given to AI Agent, from 1-5. + """ + + rating_remark: typing.Optional[str] = pydantic.Field(default=None) + """ + The customer satisfaction rating remark given to AI Agent. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the AI agent rating was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the AI agent rating was last updated. + """ + + content_sources: typing.Optional[ContentSourcesList] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/ai_agent/types/ai_agent_last_answer_type.py b/src/intercom/ai_agent/types/ai_agent_last_answer_type.py new file mode 100644 index 00000000..ab8e9a15 --- /dev/null +++ b/src/intercom/ai_agent/types/ai_agent_last_answer_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AiAgentLastAnswerType = typing.Union[typing.Literal["ai_answer", "custom_answer"], typing.Any] diff --git a/src/intercom/ai_agent/types/ai_agent_resolution_state.py b/src/intercom/ai_agent/types/ai_agent_resolution_state.py new file mode 100644 index 00000000..2c5922dd --- /dev/null +++ b/src/intercom/ai_agent/types/ai_agent_resolution_state.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AiAgentResolutionState = typing.Union[ + typing.Literal["assumed_resolution", "confirmed_resolution", "routed_to_team", "abandoned"], typing.Any +] diff --git a/src/intercom/ai_agent/types/ai_agent_source_type.py b/src/intercom/ai_agent/types/ai_agent_source_type.py new file mode 100644 index 00000000..f108bc8c --- /dev/null +++ b/src/intercom/ai_agent/types/ai_agent_source_type.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AiAgentSourceType = typing.Union[ + typing.Literal["essentials_plan_setup", "profile", "workflow", "workflow_preview", "fin_preview"], typing.Any +] diff --git a/src/intercom/ai_content/__init__.py b/src/intercom/ai_content/__init__.py new file mode 100644 index 00000000..c16e040f --- /dev/null +++ b/src/intercom/ai_content/__init__.py @@ -0,0 +1,64 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + ContentImportSource, + ContentImportSourceStatus, + ContentImportSourceSyncBehavior, + ContentImportSourcesList, + CreateContentImportSourceRequestStatus, + ExternalPage, + ExternalPagesList, + UpdateContentImportSourceRequestStatus, + UpdateContentImportSourceRequestSyncBehavior, + ) +_dynamic_imports: typing.Dict[str, str] = { + "ContentImportSource": ".types", + "ContentImportSourceStatus": ".types", + "ContentImportSourceSyncBehavior": ".types", + "ContentImportSourcesList": ".types", + "CreateContentImportSourceRequestStatus": ".types", + "ExternalPage": ".types", + "ExternalPagesList": ".types", + "UpdateContentImportSourceRequestStatus": ".types", + "UpdateContentImportSourceRequestSyncBehavior": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "ContentImportSource", + "ContentImportSourceStatus", + "ContentImportSourceSyncBehavior", + "ContentImportSourcesList", + "CreateContentImportSourceRequestStatus", + "ExternalPage", + "ExternalPagesList", + "UpdateContentImportSourceRequestStatus", + "UpdateContentImportSourceRequestSyncBehavior", +] diff --git a/src/intercom/ai_content/client.py b/src/intercom/ai_content/client.py new file mode 100644 index 00000000..5536ba69 --- /dev/null +++ b/src/intercom/ai_content/client.py @@ -0,0 +1,979 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawAiContentClient, RawAiContentClient +from .types.content_import_source import ContentImportSource +from .types.content_import_sources_list import ContentImportSourcesList +from .types.create_content_import_source_request_status import CreateContentImportSourceRequestStatus +from .types.external_page import ExternalPage +from .types.external_pages_list import ExternalPagesList +from .types.update_content_import_source_request_status import UpdateContentImportSourceRequestStatus +from .types.update_content_import_source_request_sync_behavior import UpdateContentImportSourceRequestSyncBehavior + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class AiContentClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawAiContentClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawAiContentClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawAiContentClient + """ + return self._raw_client + + def list_content_import_sources( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContentImportSourcesList: + """ + You can retrieve a list of all content import sources for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContentImportSourcesList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.ai_content.list_content_import_sources() + """ + _response = self._raw_client.list_content_import_sources(request_options=request_options) + return _response.data + + def create_content_import_source( + self, + *, + url: str, + status: typing.Optional[CreateContentImportSourceRequestStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ContentImportSource: + """ + You can create a new content import source by sending a POST request to this endpoint. + + Parameters + ---------- + url : str + The URL of the content import source. + + status : typing.Optional[CreateContentImportSourceRequestStatus] + The status of the content import source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContentImportSource + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.ai_content.create_content_import_source( + url="https://www.example.com", + ) + """ + _response = self._raw_client.create_content_import_source( + url=url, status=status, request_options=request_options + ) + return _response.data + + def get_content_import_source( + self, source_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContentImportSource: + """ + Parameters + ---------- + source_id : str + The unique identifier for the content import source which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContentImportSource + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.ai_content.get_content_import_source( + source_id="source_id", + ) + """ + _response = self._raw_client.get_content_import_source(source_id, request_options=request_options) + return _response.data + + def update_content_import_source( + self, + source_id: str, + *, + sync_behavior: UpdateContentImportSourceRequestSyncBehavior, + url: str, + status: typing.Optional[UpdateContentImportSourceRequestStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ContentImportSource: + """ + You can update an existing content import source. + + Parameters + ---------- + source_id : str + The unique identifier for the content import source which is given by Intercom. + + sync_behavior : UpdateContentImportSourceRequestSyncBehavior + If you intend to create or update External Pages via the API, this should be set to `api`. You can not change the value to or from api. + + url : str + The URL of the content import source. This may only be different from the existing value if the sync behavior is API. + + status : typing.Optional[UpdateContentImportSourceRequestStatus] + The status of the content import source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContentImportSource + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.ai_content.update_content_import_source( + source_id="source_id", + sync_behavior="api", + url="https://www.example.com", + ) + """ + _response = self._raw_client.update_content_import_source( + source_id, sync_behavior=sync_behavior, url=url, status=status, request_options=request_options + ) + return _response.data + + def delete_content_import_source( + self, source_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + You can delete a content import source by making a DELETE request this endpoint. This will also delete all external pages that were imported from this source. + + Parameters + ---------- + source_id : str + The unique identifier for the content import source which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.ai_content.delete_content_import_source( + source_id="source_id", + ) + """ + _response = self._raw_client.delete_content_import_source(source_id, request_options=request_options) + return _response.data + + def list_external_pages(self, *, request_options: typing.Optional[RequestOptions] = None) -> ExternalPagesList: + """ + You can retrieve a list of all external pages for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ExternalPagesList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.ai_content.list_external_pages() + """ + _response = self._raw_client.list_external_pages(request_options=request_options) + return _response.data + + def create_external_page( + self, + *, + title: str, + html: str, + source_id: int, + external_id: str, + url: typing.Optional[str] = OMIT, + ai_agent_availability: typing.Optional[bool] = OMIT, + ai_copilot_availability: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ExternalPage: + """ + You can create a new external page by sending a POST request to this endpoint. If an external page already exists with the specified source_id and external_id, it will be updated instead. + + Parameters + ---------- + title : str + The title of the external page. + + html : str + The body of the external page in HTML. + + source_id : int + The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + + external_id : str + The identifier for the external page which was given by the source. Must be unique for the source. + + url : typing.Optional[str] + The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. When a URL is not present, Fin will not reference the source. + + ai_agent_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by AI Agent. Will not default when updating an existing external page. + + ai_copilot_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by AI Copilot. Will not default when updating an existing external page. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ExternalPage + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.ai_content.create_external_page( + title="Test", + html="

Test

", + url="https://www.example.com", + source_id=44, + external_id="abc1234", + ) + """ + _response = self._raw_client.create_external_page( + title=title, + html=html, + source_id=source_id, + external_id=external_id, + url=url, + ai_agent_availability=ai_agent_availability, + ai_copilot_availability=ai_copilot_availability, + request_options=request_options, + ) + return _response.data + + def get_external_page( + self, page_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ExternalPage: + """ + You can retrieve an external page. + + Parameters + ---------- + page_id : str + The unique identifier for the external page which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ExternalPage + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.ai_content.get_external_page( + page_id="page_id", + ) + """ + _response = self._raw_client.get_external_page(page_id, request_options=request_options) + return _response.data + + def update_external_page( + self, + page_id: str, + *, + title: str, + html: str, + url: str, + source_id: int, + fin_availability: typing.Optional[bool] = OMIT, + external_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ExternalPage: + """ + You can update an existing external page (if it was created via the API). + + Parameters + ---------- + page_id : str + The unique identifier for the external page which is given by Intercom. + + title : str + The title of the external page. + + html : str + The body of the external page in HTML. + + url : str + The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. + + source_id : int + The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + + fin_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by Fin. + + external_id : typing.Optional[str] + The identifier for the external page which was given by the source. Must be unique for the source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ExternalPage + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.ai_content.update_external_page( + page_id="page_id", + title="Test", + html="

Test

", + url="https://www.example.com", + source_id=47, + external_id="5678", + ) + """ + _response = self._raw_client.update_external_page( + page_id, + title=title, + html=html, + url=url, + source_id=source_id, + fin_availability=fin_availability, + external_id=external_id, + request_options=request_options, + ) + return _response.data + + def delete_external_page( + self, page_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ExternalPage: + """ + Sending a DELETE request for an external page will remove it from the content library UI and from being used for AI answers. + + Parameters + ---------- + page_id : str + The unique identifier for the external page which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ExternalPage + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.ai_content.delete_external_page( + page_id="page_id", + ) + """ + _response = self._raw_client.delete_external_page(page_id, request_options=request_options) + return _response.data + + +class AsyncAiContentClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawAiContentClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawAiContentClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawAiContentClient + """ + return self._raw_client + + async def list_content_import_sources( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContentImportSourcesList: + """ + You can retrieve a list of all content import sources for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContentImportSourcesList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.ai_content.list_content_import_sources() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_content_import_sources(request_options=request_options) + return _response.data + + async def create_content_import_source( + self, + *, + url: str, + status: typing.Optional[CreateContentImportSourceRequestStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ContentImportSource: + """ + You can create a new content import source by sending a POST request to this endpoint. + + Parameters + ---------- + url : str + The URL of the content import source. + + status : typing.Optional[CreateContentImportSourceRequestStatus] + The status of the content import source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContentImportSource + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.ai_content.create_content_import_source( + url="https://www.example.com", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_content_import_source( + url=url, status=status, request_options=request_options + ) + return _response.data + + async def get_content_import_source( + self, source_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContentImportSource: + """ + Parameters + ---------- + source_id : str + The unique identifier for the content import source which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContentImportSource + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.ai_content.get_content_import_source( + source_id="source_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_content_import_source(source_id, request_options=request_options) + return _response.data + + async def update_content_import_source( + self, + source_id: str, + *, + sync_behavior: UpdateContentImportSourceRequestSyncBehavior, + url: str, + status: typing.Optional[UpdateContentImportSourceRequestStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ContentImportSource: + """ + You can update an existing content import source. + + Parameters + ---------- + source_id : str + The unique identifier for the content import source which is given by Intercom. + + sync_behavior : UpdateContentImportSourceRequestSyncBehavior + If you intend to create or update External Pages via the API, this should be set to `api`. You can not change the value to or from api. + + url : str + The URL of the content import source. This may only be different from the existing value if the sync behavior is API. + + status : typing.Optional[UpdateContentImportSourceRequestStatus] + The status of the content import source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContentImportSource + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.ai_content.update_content_import_source( + source_id="source_id", + sync_behavior="api", + url="https://www.example.com", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update_content_import_source( + source_id, sync_behavior=sync_behavior, url=url, status=status, request_options=request_options + ) + return _response.data + + async def delete_content_import_source( + self, source_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + You can delete a content import source by making a DELETE request this endpoint. This will also delete all external pages that were imported from this source. + + Parameters + ---------- + source_id : str + The unique identifier for the content import source which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.ai_content.delete_content_import_source( + source_id="source_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_content_import_source(source_id, request_options=request_options) + return _response.data + + async def list_external_pages( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> ExternalPagesList: + """ + You can retrieve a list of all external pages for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ExternalPagesList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.ai_content.list_external_pages() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_external_pages(request_options=request_options) + return _response.data + + async def create_external_page( + self, + *, + title: str, + html: str, + source_id: int, + external_id: str, + url: typing.Optional[str] = OMIT, + ai_agent_availability: typing.Optional[bool] = OMIT, + ai_copilot_availability: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ExternalPage: + """ + You can create a new external page by sending a POST request to this endpoint. If an external page already exists with the specified source_id and external_id, it will be updated instead. + + Parameters + ---------- + title : str + The title of the external page. + + html : str + The body of the external page in HTML. + + source_id : int + The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + + external_id : str + The identifier for the external page which was given by the source. Must be unique for the source. + + url : typing.Optional[str] + The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. When a URL is not present, Fin will not reference the source. + + ai_agent_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by AI Agent. Will not default when updating an existing external page. + + ai_copilot_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by AI Copilot. Will not default when updating an existing external page. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ExternalPage + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.ai_content.create_external_page( + title="Test", + html="

Test

", + url="https://www.example.com", + source_id=44, + external_id="abc1234", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_external_page( + title=title, + html=html, + source_id=source_id, + external_id=external_id, + url=url, + ai_agent_availability=ai_agent_availability, + ai_copilot_availability=ai_copilot_availability, + request_options=request_options, + ) + return _response.data + + async def get_external_page( + self, page_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ExternalPage: + """ + You can retrieve an external page. + + Parameters + ---------- + page_id : str + The unique identifier for the external page which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ExternalPage + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.ai_content.get_external_page( + page_id="page_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_external_page(page_id, request_options=request_options) + return _response.data + + async def update_external_page( + self, + page_id: str, + *, + title: str, + html: str, + url: str, + source_id: int, + fin_availability: typing.Optional[bool] = OMIT, + external_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ExternalPage: + """ + You can update an existing external page (if it was created via the API). + + Parameters + ---------- + page_id : str + The unique identifier for the external page which is given by Intercom. + + title : str + The title of the external page. + + html : str + The body of the external page in HTML. + + url : str + The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. + + source_id : int + The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + + fin_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by Fin. + + external_id : typing.Optional[str] + The identifier for the external page which was given by the source. Must be unique for the source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ExternalPage + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.ai_content.update_external_page( + page_id="page_id", + title="Test", + html="

Test

", + url="https://www.example.com", + source_id=47, + external_id="5678", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update_external_page( + page_id, + title=title, + html=html, + url=url, + source_id=source_id, + fin_availability=fin_availability, + external_id=external_id, + request_options=request_options, + ) + return _response.data + + async def delete_external_page( + self, page_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ExternalPage: + """ + Sending a DELETE request for an external page will remove it from the content library UI and from being used for AI answers. + + Parameters + ---------- + page_id : str + The unique identifier for the external page which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ExternalPage + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.ai_content.delete_external_page( + page_id="page_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_external_page(page_id, request_options=request_options) + return _response.data diff --git a/src/intercom/ai_content/raw_client.py b/src/intercom/ai_content/raw_client.py new file mode 100644 index 00000000..13be7f3a --- /dev/null +++ b/src/intercom/ai_content/raw_client.py @@ -0,0 +1,1243 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ..core.unchecked_base_model import construct_type +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from .types.content_import_source import ContentImportSource +from .types.content_import_sources_list import ContentImportSourcesList +from .types.create_content_import_source_request_status import CreateContentImportSourceRequestStatus +from .types.external_page import ExternalPage +from .types.external_pages_list import ExternalPagesList +from .types.update_content_import_source_request_status import UpdateContentImportSourceRequestStatus +from .types.update_content_import_source_request_sync_behavior import UpdateContentImportSourceRequestSyncBehavior + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawAiContentClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_content_import_sources( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ContentImportSourcesList]: + """ + You can retrieve a list of all content import sources for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ContentImportSourcesList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "ai/content_import_sources", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ContentImportSourcesList, + construct_type( + type_=ContentImportSourcesList, # 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 create_content_import_source( + self, + *, + url: str, + status: typing.Optional[CreateContentImportSourceRequestStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ContentImportSource]: + """ + You can create a new content import source by sending a POST request to this endpoint. + + Parameters + ---------- + url : str + The URL of the content import source. + + status : typing.Optional[CreateContentImportSourceRequestStatus] + The status of the content import source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ContentImportSource] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "ai/content_import_sources", + method="POST", + json={ + "status": status, + "url": url, + "sync_behavior": "api", + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ContentImportSource, + construct_type( + type_=ContentImportSource, # 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 get_content_import_source( + self, source_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ContentImportSource]: + """ + Parameters + ---------- + source_id : str + The unique identifier for the content import source which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ContentImportSource] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"ai/content_import_sources/{jsonable_encoder(source_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ContentImportSource, + construct_type( + type_=ContentImportSource, # 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_content_import_source( + self, + source_id: str, + *, + sync_behavior: UpdateContentImportSourceRequestSyncBehavior, + url: str, + status: typing.Optional[UpdateContentImportSourceRequestStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ContentImportSource]: + """ + You can update an existing content import source. + + Parameters + ---------- + source_id : str + The unique identifier for the content import source which is given by Intercom. + + sync_behavior : UpdateContentImportSourceRequestSyncBehavior + If you intend to create or update External Pages via the API, this should be set to `api`. You can not change the value to or from api. + + url : str + The URL of the content import source. This may only be different from the existing value if the sync behavior is API. + + status : typing.Optional[UpdateContentImportSourceRequestStatus] + The status of the content import source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ContentImportSource] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"ai/content_import_sources/{jsonable_encoder(source_id)}", + method="PUT", + json={ + "sync_behavior": sync_behavior, + "status": status, + "url": url, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ContentImportSource, + construct_type( + type_=ContentImportSource, # 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_content_import_source( + self, source_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[None]: + """ + You can delete a content import source by making a DELETE request this endpoint. This will also delete all external pages that were imported from this source. + + Parameters + ---------- + source_id : str + The unique identifier for the content import source which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + f"ai/content_import_sources/{jsonable_encoder(source_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + 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_external_pages( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ExternalPagesList]: + """ + You can retrieve a list of all external pages for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ExternalPagesList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "ai/external_pages", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ExternalPagesList, + construct_type( + type_=ExternalPagesList, # 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 create_external_page( + self, + *, + title: str, + html: str, + source_id: int, + external_id: str, + url: typing.Optional[str] = OMIT, + ai_agent_availability: typing.Optional[bool] = OMIT, + ai_copilot_availability: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ExternalPage]: + """ + You can create a new external page by sending a POST request to this endpoint. If an external page already exists with the specified source_id and external_id, it will be updated instead. + + Parameters + ---------- + title : str + The title of the external page. + + html : str + The body of the external page in HTML. + + source_id : int + The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + + external_id : str + The identifier for the external page which was given by the source. Must be unique for the source. + + url : typing.Optional[str] + The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. When a URL is not present, Fin will not reference the source. + + ai_agent_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by AI Agent. Will not default when updating an existing external page. + + ai_copilot_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by AI Copilot. Will not default when updating an existing external page. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ExternalPage] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "ai/external_pages", + method="POST", + json={ + "title": title, + "html": html, + "url": url, + "ai_agent_availability": ai_agent_availability, + "ai_copilot_availability": ai_copilot_availability, + "source_id": source_id, + "external_id": external_id, + "locale": "en", + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ExternalPage, + construct_type( + type_=ExternalPage, # 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 get_external_page( + self, page_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ExternalPage]: + """ + You can retrieve an external page. + + Parameters + ---------- + page_id : str + The unique identifier for the external page which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ExternalPage] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"ai/external_pages/{jsonable_encoder(page_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ExternalPage, + construct_type( + type_=ExternalPage, # 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_external_page( + self, + page_id: str, + *, + title: str, + html: str, + url: str, + source_id: int, + fin_availability: typing.Optional[bool] = OMIT, + external_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ExternalPage]: + """ + You can update an existing external page (if it was created via the API). + + Parameters + ---------- + page_id : str + The unique identifier for the external page which is given by Intercom. + + title : str + The title of the external page. + + html : str + The body of the external page in HTML. + + url : str + The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. + + source_id : int + The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + + fin_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by Fin. + + external_id : typing.Optional[str] + The identifier for the external page which was given by the source. Must be unique for the source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ExternalPage] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"ai/external_pages/{jsonable_encoder(page_id)}", + method="PUT", + json={ + "title": title, + "html": html, + "url": url, + "fin_availability": fin_availability, + "source_id": source_id, + "external_id": external_id, + "locale": "en", + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ExternalPage, + construct_type( + type_=ExternalPage, # 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_external_page( + self, page_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ExternalPage]: + """ + Sending a DELETE request for an external page will remove it from the content library UI and from being used for AI answers. + + Parameters + ---------- + page_id : str + The unique identifier for the external page which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ExternalPage] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"ai/external_pages/{jsonable_encoder(page_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ExternalPage, + construct_type( + type_=ExternalPage, # 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) + + +class AsyncRawAiContentClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_content_import_sources( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ContentImportSourcesList]: + """ + You can retrieve a list of all content import sources for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ContentImportSourcesList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "ai/content_import_sources", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ContentImportSourcesList, + construct_type( + type_=ContentImportSourcesList, # 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 create_content_import_source( + self, + *, + url: str, + status: typing.Optional[CreateContentImportSourceRequestStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ContentImportSource]: + """ + You can create a new content import source by sending a POST request to this endpoint. + + Parameters + ---------- + url : str + The URL of the content import source. + + status : typing.Optional[CreateContentImportSourceRequestStatus] + The status of the content import source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ContentImportSource] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "ai/content_import_sources", + method="POST", + json={ + "status": status, + "url": url, + "sync_behavior": "api", + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ContentImportSource, + construct_type( + type_=ContentImportSource, # 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 get_content_import_source( + self, source_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ContentImportSource]: + """ + Parameters + ---------- + source_id : str + The unique identifier for the content import source which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ContentImportSource] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"ai/content_import_sources/{jsonable_encoder(source_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ContentImportSource, + construct_type( + type_=ContentImportSource, # 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_content_import_source( + self, + source_id: str, + *, + sync_behavior: UpdateContentImportSourceRequestSyncBehavior, + url: str, + status: typing.Optional[UpdateContentImportSourceRequestStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ContentImportSource]: + """ + You can update an existing content import source. + + Parameters + ---------- + source_id : str + The unique identifier for the content import source which is given by Intercom. + + sync_behavior : UpdateContentImportSourceRequestSyncBehavior + If you intend to create or update External Pages via the API, this should be set to `api`. You can not change the value to or from api. + + url : str + The URL of the content import source. This may only be different from the existing value if the sync behavior is API. + + status : typing.Optional[UpdateContentImportSourceRequestStatus] + The status of the content import source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ContentImportSource] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"ai/content_import_sources/{jsonable_encoder(source_id)}", + method="PUT", + json={ + "sync_behavior": sync_behavior, + "status": status, + "url": url, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ContentImportSource, + construct_type( + type_=ContentImportSource, # 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_content_import_source( + self, source_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + You can delete a content import source by making a DELETE request this endpoint. This will also delete all external pages that were imported from this source. + + Parameters + ---------- + source_id : str + The unique identifier for the content import source which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + f"ai/content_import_sources/{jsonable_encoder(source_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + 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_external_pages( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ExternalPagesList]: + """ + You can retrieve a list of all external pages for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ExternalPagesList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "ai/external_pages", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ExternalPagesList, + construct_type( + type_=ExternalPagesList, # 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 create_external_page( + self, + *, + title: str, + html: str, + source_id: int, + external_id: str, + url: typing.Optional[str] = OMIT, + ai_agent_availability: typing.Optional[bool] = OMIT, + ai_copilot_availability: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ExternalPage]: + """ + You can create a new external page by sending a POST request to this endpoint. If an external page already exists with the specified source_id and external_id, it will be updated instead. + + Parameters + ---------- + title : str + The title of the external page. + + html : str + The body of the external page in HTML. + + source_id : int + The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + + external_id : str + The identifier for the external page which was given by the source. Must be unique for the source. + + url : typing.Optional[str] + The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. When a URL is not present, Fin will not reference the source. + + ai_agent_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by AI Agent. Will not default when updating an existing external page. + + ai_copilot_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by AI Copilot. Will not default when updating an existing external page. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ExternalPage] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "ai/external_pages", + method="POST", + json={ + "title": title, + "html": html, + "url": url, + "ai_agent_availability": ai_agent_availability, + "ai_copilot_availability": ai_copilot_availability, + "source_id": source_id, + "external_id": external_id, + "locale": "en", + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ExternalPage, + construct_type( + type_=ExternalPage, # 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 get_external_page( + self, page_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ExternalPage]: + """ + You can retrieve an external page. + + Parameters + ---------- + page_id : str + The unique identifier for the external page which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ExternalPage] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"ai/external_pages/{jsonable_encoder(page_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ExternalPage, + construct_type( + type_=ExternalPage, # 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_external_page( + self, + page_id: str, + *, + title: str, + html: str, + url: str, + source_id: int, + fin_availability: typing.Optional[bool] = OMIT, + external_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ExternalPage]: + """ + You can update an existing external page (if it was created via the API). + + Parameters + ---------- + page_id : str + The unique identifier for the external page which is given by Intercom. + + title : str + The title of the external page. + + html : str + The body of the external page in HTML. + + url : str + The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. + + source_id : int + The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + + fin_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by Fin. + + external_id : typing.Optional[str] + The identifier for the external page which was given by the source. Must be unique for the source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ExternalPage] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"ai/external_pages/{jsonable_encoder(page_id)}", + method="PUT", + json={ + "title": title, + "html": html, + "url": url, + "fin_availability": fin_availability, + "source_id": source_id, + "external_id": external_id, + "locale": "en", + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ExternalPage, + construct_type( + type_=ExternalPage, # 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_external_page( + self, page_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ExternalPage]: + """ + Sending a DELETE request for an external page will remove it from the content library UI and from being used for AI answers. + + Parameters + ---------- + page_id : str + The unique identifier for the external page which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ExternalPage] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"ai/external_pages/{jsonable_encoder(page_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ExternalPage, + construct_type( + type_=ExternalPage, # 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) diff --git a/src/intercom/ai_content/types/__init__.py b/src/intercom/ai_content/types/__init__.py new file mode 100644 index 00000000..59fd64a1 --- /dev/null +++ b/src/intercom/ai_content/types/__init__.py @@ -0,0 +1,62 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .content_import_source import ContentImportSource + from .content_import_source_status import ContentImportSourceStatus + from .content_import_source_sync_behavior import ContentImportSourceSyncBehavior + from .content_import_sources_list import ContentImportSourcesList + from .create_content_import_source_request_status import CreateContentImportSourceRequestStatus + from .external_page import ExternalPage + from .external_pages_list import ExternalPagesList + from .update_content_import_source_request_status import UpdateContentImportSourceRequestStatus + from .update_content_import_source_request_sync_behavior import UpdateContentImportSourceRequestSyncBehavior +_dynamic_imports: typing.Dict[str, str] = { + "ContentImportSource": ".content_import_source", + "ContentImportSourceStatus": ".content_import_source_status", + "ContentImportSourceSyncBehavior": ".content_import_source_sync_behavior", + "ContentImportSourcesList": ".content_import_sources_list", + "CreateContentImportSourceRequestStatus": ".create_content_import_source_request_status", + "ExternalPage": ".external_page", + "ExternalPagesList": ".external_pages_list", + "UpdateContentImportSourceRequestStatus": ".update_content_import_source_request_status", + "UpdateContentImportSourceRequestSyncBehavior": ".update_content_import_source_request_sync_behavior", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "ContentImportSource", + "ContentImportSourceStatus", + "ContentImportSourceSyncBehavior", + "ContentImportSourcesList", + "CreateContentImportSourceRequestStatus", + "ExternalPage", + "ExternalPagesList", + "UpdateContentImportSourceRequestStatus", + "UpdateContentImportSourceRequestSyncBehavior", +] diff --git a/src/intercom/ai_content/types/content_import_source.py b/src/intercom/ai_content/types/content_import_source.py new file mode 100644 index 00000000..474a4d2c --- /dev/null +++ b/src/intercom/ai_content/types/content_import_source.py @@ -0,0 +1,64 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .content_import_source_status import ContentImportSourceStatus +from .content_import_source_sync_behavior import ContentImportSourceSyncBehavior + + +class ContentImportSource(UncheckedBaseModel): + """ + An external source for External Pages that you add to your Fin Content Library. + """ + + type: typing.Literal["content_import_source"] = pydantic.Field(default="content_import_source") + """ + Always external_page + """ + + id: int = pydantic.Field() + """ + The unique identifier for the content import source which is given by Intercom. + """ + + last_synced_at: int = pydantic.Field() + """ + The time when the content import source was last synced. + """ + + sync_behavior: ContentImportSourceSyncBehavior = pydantic.Field() + """ + If you intend to create or update External Pages via the API, this should be set to `api`. + """ + + status: ContentImportSourceStatus = pydantic.Field() + """ + The status of the content import source. + """ + + url: str = pydantic.Field() + """ + The URL of the root of the external source. + """ + + created_at: int = pydantic.Field() + """ + The time when the content import source was created. + """ + + updated_at: int = pydantic.Field() + """ + The time when the content import source was last updated. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/ai_content/types/content_import_source_status.py b/src/intercom/ai_content/types/content_import_source_status.py new file mode 100644 index 00000000..389c3c83 --- /dev/null +++ b/src/intercom/ai_content/types/content_import_source_status.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ContentImportSourceStatus = typing.Union[typing.Literal["active", "deactivated"], typing.Any] diff --git a/src/intercom/ai_content/types/content_import_source_sync_behavior.py b/src/intercom/ai_content/types/content_import_source_sync_behavior.py new file mode 100644 index 00000000..97abd401 --- /dev/null +++ b/src/intercom/ai_content/types/content_import_source_sync_behavior.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ContentImportSourceSyncBehavior = typing.Union[typing.Literal["api", "automatic", "manual"], typing.Any] diff --git a/src/intercom/ai_content/types/content_import_sources_list.py b/src/intercom/ai_content/types/content_import_sources_list.py new file mode 100644 index 00000000..355cff55 --- /dev/null +++ b/src/intercom/ai_content/types/content_import_sources_list.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.pages_link import PagesLink +from .content_import_source import ContentImportSource + + +class ContentImportSourcesList(UncheckedBaseModel): + """ + This will return a list of the content import sources for the App. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object - `list`. + """ + + pages: typing.Optional[PagesLink] = None + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + A count of the total number of content import sources. + """ + + data: typing.Optional[typing.List[ContentImportSource]] = pydantic.Field(default=None) + """ + An array of Content Import Source objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/ai_content/types/create_content_import_source_request_status.py b/src/intercom/ai_content/types/create_content_import_source_request_status.py new file mode 100644 index 00000000..047dfd52 --- /dev/null +++ b/src/intercom/ai_content/types/create_content_import_source_request_status.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CreateContentImportSourceRequestStatus = typing.Union[typing.Literal["active", "deactivated"], typing.Any] diff --git a/src/intercom/ai_content/types/external_page.py b/src/intercom/ai_content/types/external_page.py new file mode 100644 index 00000000..bdb55e76 --- /dev/null +++ b/src/intercom/ai_content/types/external_page.py @@ -0,0 +1,92 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class ExternalPage(UncheckedBaseModel): + """ + External pages that you have added to your Fin Content Library. + """ + + type: typing.Literal["external_page"] = pydantic.Field(default="external_page") + """ + Always external_page + """ + + id: str = pydantic.Field() + """ + The unique identifier for the external page which is given by Intercom. + """ + + title: str = pydantic.Field() + """ + The title of the external page. + """ + + html: str = pydantic.Field() + """ + The body of the external page in HTML. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. + """ + + ai_agent_availability: bool = pydantic.Field() + """ + Whether the external page should be used to answer questions by AI Agent. + """ + + ai_copilot_availability: bool = pydantic.Field() + """ + Whether the external page should be used to answer questions by AI Copilot. + """ + + fin_availability: typing.Optional[bool] = pydantic.Field(default=None) + """ + Deprecated. Use ai_agent_availability and ai_copilot_availability instead. + """ + + locale: typing.Literal["en"] = pydantic.Field(default="en") + """ + Always en + """ + + source_id: int = pydantic.Field() + """ + The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + """ + + external_id: str = pydantic.Field() + """ + The identifier for the external page which was given by the source. Must be unique for the source. + """ + + created_at: int = pydantic.Field() + """ + The time when the external page was created. + """ + + updated_at: int = pydantic.Field() + """ + The time when the external page was last updated. + """ + + last_ingested_at: int = pydantic.Field() + """ + The time when the external page was last ingested. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/ai_content/types/external_pages_list.py b/src/intercom/ai_content/types/external_pages_list.py new file mode 100644 index 00000000..47b210a4 --- /dev/null +++ b/src/intercom/ai_content/types/external_pages_list.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.pages_link import PagesLink +from .external_page import ExternalPage + + +class ExternalPagesList(UncheckedBaseModel): + """ + This will return a list of external pages for the App. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object - `list`. + """ + + pages: typing.Optional[PagesLink] = None + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + A count of the total number of external pages. + """ + + data: typing.Optional[typing.List[ExternalPage]] = pydantic.Field(default=None) + """ + An array of External Page objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/ai_content/types/update_content_import_source_request_status.py b/src/intercom/ai_content/types/update_content_import_source_request_status.py new file mode 100644 index 00000000..080fcef0 --- /dev/null +++ b/src/intercom/ai_content/types/update_content_import_source_request_status.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +UpdateContentImportSourceRequestStatus = typing.Union[typing.Literal["active", "deactivated"], typing.Any] diff --git a/src/intercom/ai_content/types/update_content_import_source_request_sync_behavior.py b/src/intercom/ai_content/types/update_content_import_source_request_sync_behavior.py new file mode 100644 index 00000000..19fc0b06 --- /dev/null +++ b/src/intercom/ai_content/types/update_content_import_source_request_sync_behavior.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +UpdateContentImportSourceRequestSyncBehavior = typing.Union[typing.Literal["api", "automated", "manual"], typing.Any] diff --git a/src/intercom/ai_content_source/__init__.py b/src/intercom/ai_content_source/__init__.py new file mode 100644 index 00000000..31126304 --- /dev/null +++ b/src/intercom/ai_content_source/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ContentSource +_dynamic_imports: typing.Dict[str, str] = {"ContentSource": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["ContentSource"] diff --git a/src/intercom/ai_content_source/types/__init__.py b/src/intercom/ai_content_source/types/__init__.py new file mode 100644 index 00000000..6791ef75 --- /dev/null +++ b/src/intercom/ai_content_source/types/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .content_source import ContentSource +_dynamic_imports: typing.Dict[str, str] = {"ContentSource": ".content_source"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["ContentSource"] diff --git a/src/intercom/ai_content_source/types/content_source.py b/src/intercom/ai_content_source/types/content_source.py new file mode 100644 index 00000000..28316bc5 --- /dev/null +++ b/src/intercom/ai_content_source/types/content_source.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class ContentSource(UncheckedBaseModel): + """ + The content source used by AI Agent in the conversation. + """ + + content_type: typing.Optional[typing.Literal["custom_answer"]] = pydantic.Field(default=None) + """ + The type of the content source. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The internal URL linking to the content source for teammates. + """ + + title: typing.Optional[str] = pydantic.Field(default=None) + """ + The title of the content source. + """ + + locale: typing.Optional[str] = pydantic.Field(default=None) + """ + The ISO 639 language code of the content source. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/articles/__init__.py b/src/intercom/articles/__init__.py new file mode 100644 index 00000000..027fb0f1 --- /dev/null +++ b/src/intercom/articles/__init__.py @@ -0,0 +1,73 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + Article, + ArticleListItem, + ArticleListItemState, + ArticleSearchHighlights, + ArticleSearchHighlightsHighlightedSummaryItemItem, + ArticleSearchHighlightsHighlightedSummaryItemItemType, + ArticleSearchHighlightsHighlightedTitleItem, + ArticleSearchHighlightsHighlightedTitleItemType, + ArticleSearchResponse, + ArticleSearchResponseData, + InternalArticle, + UpdateArticleRequestState, + ) +_dynamic_imports: typing.Dict[str, str] = { + "Article": ".types", + "ArticleListItem": ".types", + "ArticleListItemState": ".types", + "ArticleSearchHighlights": ".types", + "ArticleSearchHighlightsHighlightedSummaryItemItem": ".types", + "ArticleSearchHighlightsHighlightedSummaryItemItemType": ".types", + "ArticleSearchHighlightsHighlightedTitleItem": ".types", + "ArticleSearchHighlightsHighlightedTitleItemType": ".types", + "ArticleSearchResponse": ".types", + "ArticleSearchResponseData": ".types", + "InternalArticle": ".types", + "UpdateArticleRequestState": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "Article", + "ArticleListItem", + "ArticleListItemState", + "ArticleSearchHighlights", + "ArticleSearchHighlightsHighlightedSummaryItemItem", + "ArticleSearchHighlightsHighlightedSummaryItemItemType", + "ArticleSearchHighlightsHighlightedTitleItem", + "ArticleSearchHighlightsHighlightedTitleItemType", + "ArticleSearchResponse", + "ArticleSearchResponseData", + "InternalArticle", + "UpdateArticleRequestState", +] diff --git a/src/intercom/articles/client.py b/src/intercom/articles/client.py new file mode 100644 index 00000000..f1392ba1 --- /dev/null +++ b/src/intercom/articles/client.py @@ -0,0 +1,708 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.pagination import AsyncPager, SyncPager +from ..core.request_options import RequestOptions +from ..types.article_list import ArticleList +from ..types.article_translated_content import ArticleTranslatedContent +from ..types.create_article_request import CreateArticleRequest +from ..types.deleted_article_object import DeletedArticleObject +from .raw_client import AsyncRawArticlesClient, RawArticlesClient +from .types.article import Article +from .types.article_list_item import ArticleListItem +from .types.article_search_response import ArticleSearchResponse +from .types.update_article_request_state import UpdateArticleRequestState + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class ArticlesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawArticlesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawArticlesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawArticlesClient + """ + return self._raw_client + + def list( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[ArticleListItem, ArticleList]: + """ + You can fetch a list of all articles by making a GET request to `https://api.intercom.io/articles`. + + > 📘 How are the articles sorted and ordered? + > + > Articles will be returned in descending order on the `updated_at` attribute. This means if you need to iterate through results then we'll show the most recently updated articles first. + + 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 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[ArticleListItem, ArticleList] + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + response = client.articles.list() + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.list(page=page, per_page=per_page, request_options=request_options) + + def create( + self, + *, + request: typing.Optional[CreateArticleRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Article: + """ + You can create a new article by making a POST request to `https://api.intercom.io/articles`. + + Parameters + ---------- + request : typing.Optional[CreateArticleRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Article + article created + + Examples + -------- + from intercom import ( + ArticleContent, + ArticleTranslatedContent, + CreateArticleRequest, + Intercom, + ) + + client = Intercom( + token="YOUR_TOKEN", + ) + client.articles.create( + request=CreateArticleRequest( + title="Thanks for everything", + description="Description of the Article", + body="Body of the Article", + author_id=991267497, + state="published", + parent_id=145, + parent_type="collection", + translated_content=ArticleTranslatedContent( + fr=ArticleContent( + title="Merci pour tout", + description="Description de l'article", + body="Corps de l'article", + author_id=991267497, + state="published", + ), + ), + ), + ) + """ + _response = self._raw_client.create(request=request, request_options=request_options) + return _response.data + + def find(self, article_id: int, *, request_options: typing.Optional[RequestOptions] = None) -> Article: + """ + You can fetch the details of a single article by making a GET request to `https://api.intercom.io/articles/`. + + Parameters + ---------- + article_id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Article + Article found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.articles.find( + article_id=1, + ) + """ + _response = self._raw_client.find(article_id, request_options=request_options) + return _response.data + + def update( + self, + article_id: int, + *, + title: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + body: typing.Optional[str] = OMIT, + author_id: typing.Optional[int] = OMIT, + state: typing.Optional[UpdateArticleRequestState] = OMIT, + parent_id: typing.Optional[str] = OMIT, + parent_type: typing.Optional[str] = OMIT, + translated_content: typing.Optional[ArticleTranslatedContent] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Article: + """ + You can update the details of a single article by making a PUT request to `https://api.intercom.io/articles/`. + + Parameters + ---------- + article_id : int + The unique identifier for the article which is given by Intercom. + + title : typing.Optional[str] + The title of the article.For multilingual articles, this will be the title of the default language's content. + + description : typing.Optional[str] + The description of the article. For multilingual articles, this will be the description of the default language's content. + + body : typing.Optional[str] + The content of the article. For multilingual articles, this will be the body of the default language's content. + + author_id : typing.Optional[int] + The id of the author of the article. For multilingual articles, this will be the id of the author of the default language's content. Must be a teammate on the help center's workspace. + + state : typing.Optional[UpdateArticleRequestState] + Whether the article will be `published` or will be a `draft`. Defaults to draft. For multilingual articles, this will be the state of the default language's content. + + parent_id : typing.Optional[str] + The id of the article's parent collection or section. An article without this field stands alone. + + parent_type : typing.Optional[str] + The type of parent, which can either be a `collection` or `section`. + + translated_content : typing.Optional[ArticleTranslatedContent] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Article + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.articles.update( + article_id=1, + title="Christmas is here!", + body="

New gifts in store for the jolly season

", + ) + """ + _response = self._raw_client.update( + article_id, + title=title, + description=description, + body=body, + author_id=author_id, + state=state, + parent_id=parent_id, + parent_type=parent_type, + translated_content=translated_content, + request_options=request_options, + ) + return _response.data + + def delete( + self, article_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeletedArticleObject: + """ + You can delete a single article by making a DELETE request to `https://api.intercom.io/articles/`. + + Parameters + ---------- + article_id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeletedArticleObject + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.articles.delete( + article_id=1, + ) + """ + _response = self._raw_client.delete(article_id, request_options=request_options) + return _response.data + + def search( + self, + *, + phrase: typing.Optional[str] = None, + state: typing.Optional[str] = None, + help_center_id: typing.Optional[int] = None, + highlight: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ArticleSearchResponse: + """ + You can search for articles by making a GET request to `https://api.intercom.io/articles/search`. + + Parameters + ---------- + phrase : typing.Optional[str] + The phrase within your articles to search for. + + state : typing.Optional[str] + The state of the Articles returned. One of `published`, `draft` or `all`. + + help_center_id : typing.Optional[int] + The ID of the Help Center to search in. + + highlight : typing.Optional[bool] + Return a highlighted version of the matching content within your articles. Refer to the response schema for more details. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ArticleSearchResponse + Search successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.articles.search( + phrase="Getting started", + state="published", + help_center_id=1, + highlight=True, + ) + """ + _response = self._raw_client.search( + phrase=phrase, + state=state, + help_center_id=help_center_id, + highlight=highlight, + request_options=request_options, + ) + return _response.data + + +class AsyncArticlesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawArticlesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawArticlesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawArticlesClient + """ + return self._raw_client + + async def list( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[ArticleListItem, ArticleList]: + """ + You can fetch a list of all articles by making a GET request to `https://api.intercom.io/articles`. + + > 📘 How are the articles sorted and ordered? + > + > Articles will be returned in descending order on the `updated_at` attribute. This means if you need to iterate through results then we'll show the most recently updated articles first. + + 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 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[ArticleListItem, ArticleList] + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + response = await client.articles.list() + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page + + + asyncio.run(main()) + """ + return await self._raw_client.list(page=page, per_page=per_page, request_options=request_options) + + async def create( + self, + *, + request: typing.Optional[CreateArticleRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Article: + """ + You can create a new article by making a POST request to `https://api.intercom.io/articles`. + + Parameters + ---------- + request : typing.Optional[CreateArticleRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Article + article created + + Examples + -------- + import asyncio + + from intercom import ( + ArticleContent, + ArticleTranslatedContent, + AsyncIntercom, + CreateArticleRequest, + ) + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.articles.create( + request=CreateArticleRequest( + title="Thanks for everything", + description="Description of the Article", + body="Body of the Article", + author_id=991267497, + state="published", + parent_id=145, + parent_type="collection", + translated_content=ArticleTranslatedContent( + fr=ArticleContent( + title="Merci pour tout", + description="Description de l'article", + body="Corps de l'article", + author_id=991267497, + state="published", + ), + ), + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create(request=request, request_options=request_options) + return _response.data + + async def find(self, article_id: int, *, request_options: typing.Optional[RequestOptions] = None) -> Article: + """ + You can fetch the details of a single article by making a GET request to `https://api.intercom.io/articles/`. + + Parameters + ---------- + article_id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Article + Article found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.articles.find( + article_id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.find(article_id, request_options=request_options) + return _response.data + + async def update( + self, + article_id: int, + *, + title: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + body: typing.Optional[str] = OMIT, + author_id: typing.Optional[int] = OMIT, + state: typing.Optional[UpdateArticleRequestState] = OMIT, + parent_id: typing.Optional[str] = OMIT, + parent_type: typing.Optional[str] = OMIT, + translated_content: typing.Optional[ArticleTranslatedContent] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Article: + """ + You can update the details of a single article by making a PUT request to `https://api.intercom.io/articles/`. + + Parameters + ---------- + article_id : int + The unique identifier for the article which is given by Intercom. + + title : typing.Optional[str] + The title of the article.For multilingual articles, this will be the title of the default language's content. + + description : typing.Optional[str] + The description of the article. For multilingual articles, this will be the description of the default language's content. + + body : typing.Optional[str] + The content of the article. For multilingual articles, this will be the body of the default language's content. + + author_id : typing.Optional[int] + The id of the author of the article. For multilingual articles, this will be the id of the author of the default language's content. Must be a teammate on the help center's workspace. + + state : typing.Optional[UpdateArticleRequestState] + Whether the article will be `published` or will be a `draft`. Defaults to draft. For multilingual articles, this will be the state of the default language's content. + + parent_id : typing.Optional[str] + The id of the article's parent collection or section. An article without this field stands alone. + + parent_type : typing.Optional[str] + The type of parent, which can either be a `collection` or `section`. + + translated_content : typing.Optional[ArticleTranslatedContent] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Article + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.articles.update( + article_id=1, + title="Christmas is here!", + body="

New gifts in store for the jolly season

", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update( + article_id, + title=title, + description=description, + body=body, + author_id=author_id, + state=state, + parent_id=parent_id, + parent_type=parent_type, + translated_content=translated_content, + request_options=request_options, + ) + return _response.data + + async def delete( + self, article_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeletedArticleObject: + """ + You can delete a single article by making a DELETE request to `https://api.intercom.io/articles/`. + + Parameters + ---------- + article_id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeletedArticleObject + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.articles.delete( + article_id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete(article_id, request_options=request_options) + return _response.data + + async def search( + self, + *, + phrase: typing.Optional[str] = None, + state: typing.Optional[str] = None, + help_center_id: typing.Optional[int] = None, + highlight: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ArticleSearchResponse: + """ + You can search for articles by making a GET request to `https://api.intercom.io/articles/search`. + + Parameters + ---------- + phrase : typing.Optional[str] + The phrase within your articles to search for. + + state : typing.Optional[str] + The state of the Articles returned. One of `published`, `draft` or `all`. + + help_center_id : typing.Optional[int] + The ID of the Help Center to search in. + + highlight : typing.Optional[bool] + Return a highlighted version of the matching content within your articles. Refer to the response schema for more details. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ArticleSearchResponse + Search successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.articles.search( + phrase="Getting started", + state="published", + help_center_id=1, + highlight=True, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.search( + phrase=phrase, + state=state, + help_center_id=help_center_id, + highlight=highlight, + request_options=request_options, + ) + return _response.data diff --git a/src/intercom/articles/raw_client.py b/src/intercom/articles/raw_client.py new file mode 100644 index 00000000..34aed9eb --- /dev/null +++ b/src/intercom/articles/raw_client.py @@ -0,0 +1,937 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.bad_request_error import BadRequestError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.article_list import ArticleList +from ..types.article_translated_content import ArticleTranslatedContent +from ..types.create_article_request import CreateArticleRequest +from ..types.deleted_article_object import DeletedArticleObject +from ..types.error import Error +from .types.article import Article +from .types.article_list_item import ArticleListItem +from .types.article_search_response import ArticleSearchResponse +from .types.update_article_request_state import UpdateArticleRequestState + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawArticlesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[ArticleListItem, ArticleList]: + """ + You can fetch a list of all articles by making a GET request to `https://api.intercom.io/articles`. + + > 📘 How are the articles sorted and ordered? + > + > Articles will be returned in descending order on the `updated_at` attribute. This means if you need to iterate through results then we'll show the most recently updated articles first. + + 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 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[ArticleListItem, ArticleList] + successful + """ + page = page if page is not None else 1 + + _response = self._client_wrapper.httpx_client.request( + "articles", + method="GET", + params={ + "page": page, + "per_page": per_page, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + ArticleList, + construct_type( + type_=ArticleList, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.data + _has_next = True + _get_next = lambda: self.list( + 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(), + ), + ), + ) + _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: typing.Optional[CreateArticleRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Article]: + """ + You can create a new article by making a POST request to `https://api.intercom.io/articles`. + + Parameters + ---------- + request : typing.Optional[CreateArticleRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Article] + article created + """ + _response = self._client_wrapper.httpx_client.request( + "articles", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateArticleRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Article, + construct_type( + type_=Article, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 find( + self, article_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Article]: + """ + You can fetch the details of a single article by making a GET request to `https://api.intercom.io/articles/`. + + Parameters + ---------- + article_id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Article] + Article found + """ + _response = self._client_wrapper.httpx_client.request( + f"articles/{jsonable_encoder(article_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Article, + construct_type( + type_=Article, # 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 update( + self, + article_id: int, + *, + title: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + body: typing.Optional[str] = OMIT, + author_id: typing.Optional[int] = OMIT, + state: typing.Optional[UpdateArticleRequestState] = OMIT, + parent_id: typing.Optional[str] = OMIT, + parent_type: typing.Optional[str] = OMIT, + translated_content: typing.Optional[ArticleTranslatedContent] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Article]: + """ + You can update the details of a single article by making a PUT request to `https://api.intercom.io/articles/`. + + Parameters + ---------- + article_id : int + The unique identifier for the article which is given by Intercom. + + title : typing.Optional[str] + The title of the article.For multilingual articles, this will be the title of the default language's content. + + description : typing.Optional[str] + The description of the article. For multilingual articles, this will be the description of the default language's content. + + body : typing.Optional[str] + The content of the article. For multilingual articles, this will be the body of the default language's content. + + author_id : typing.Optional[int] + The id of the author of the article. For multilingual articles, this will be the id of the author of the default language's content. Must be a teammate on the help center's workspace. + + state : typing.Optional[UpdateArticleRequestState] + Whether the article will be `published` or will be a `draft`. Defaults to draft. For multilingual articles, this will be the state of the default language's content. + + parent_id : typing.Optional[str] + The id of the article's parent collection or section. An article without this field stands alone. + + parent_type : typing.Optional[str] + The type of parent, which can either be a `collection` or `section`. + + translated_content : typing.Optional[ArticleTranslatedContent] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Article] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"articles/{jsonable_encoder(article_id)}", + method="PUT", + json={ + "title": title, + "description": description, + "body": body, + "author_id": author_id, + "state": state, + "parent_id": parent_id, + "parent_type": parent_type, + "translated_content": convert_and_respect_annotation_metadata( + object_=translated_content, annotation=ArticleTranslatedContent, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Article, + construct_type( + type_=Article, # 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 delete( + self, article_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DeletedArticleObject]: + """ + You can delete a single article by making a DELETE request to `https://api.intercom.io/articles/`. + + Parameters + ---------- + article_id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeletedArticleObject] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"articles/{jsonable_encoder(article_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeletedArticleObject, + construct_type( + type_=DeletedArticleObject, # 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 search( + self, + *, + phrase: typing.Optional[str] = None, + state: typing.Optional[str] = None, + help_center_id: typing.Optional[int] = None, + highlight: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ArticleSearchResponse]: + """ + You can search for articles by making a GET request to `https://api.intercom.io/articles/search`. + + Parameters + ---------- + phrase : typing.Optional[str] + The phrase within your articles to search for. + + state : typing.Optional[str] + The state of the Articles returned. One of `published`, `draft` or `all`. + + help_center_id : typing.Optional[int] + The ID of the Help Center to search in. + + highlight : typing.Optional[bool] + Return a highlighted version of the matching content within your articles. Refer to the response schema for more details. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ArticleSearchResponse] + Search successful + """ + _response = self._client_wrapper.httpx_client.request( + "articles/search", + method="GET", + params={ + "phrase": phrase, + "state": state, + "help_center_id": help_center_id, + "highlight": highlight, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ArticleSearchResponse, + construct_type( + type_=ArticleSearchResponse, # 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) + + +class AsyncRawArticlesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[ArticleListItem, ArticleList]: + """ + You can fetch a list of all articles by making a GET request to `https://api.intercom.io/articles`. + + > 📘 How are the articles sorted and ordered? + > + > Articles will be returned in descending order on the `updated_at` attribute. This means if you need to iterate through results then we'll show the most recently updated articles first. + + 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 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[ArticleListItem, ArticleList] + successful + """ + page = page if page is not None else 1 + + _response = await self._client_wrapper.httpx_client.request( + "articles", + method="GET", + params={ + "page": page, + "per_page": per_page, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + ArticleList, + construct_type( + type_=ArticleList, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.data + _has_next = True + + async def _get_next(): + return await self.list( + 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(), + ), + ), + ) + _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: typing.Optional[CreateArticleRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Article]: + """ + You can create a new article by making a POST request to `https://api.intercom.io/articles`. + + Parameters + ---------- + request : typing.Optional[CreateArticleRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Article] + article created + """ + _response = await self._client_wrapper.httpx_client.request( + "articles", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateArticleRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Article, + construct_type( + type_=Article, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 find( + self, article_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Article]: + """ + You can fetch the details of a single article by making a GET request to `https://api.intercom.io/articles/`. + + Parameters + ---------- + article_id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Article] + Article found + """ + _response = await self._client_wrapper.httpx_client.request( + f"articles/{jsonable_encoder(article_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Article, + construct_type( + type_=Article, # 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 update( + self, + article_id: int, + *, + title: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + body: typing.Optional[str] = OMIT, + author_id: typing.Optional[int] = OMIT, + state: typing.Optional[UpdateArticleRequestState] = OMIT, + parent_id: typing.Optional[str] = OMIT, + parent_type: typing.Optional[str] = OMIT, + translated_content: typing.Optional[ArticleTranslatedContent] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Article]: + """ + You can update the details of a single article by making a PUT request to `https://api.intercom.io/articles/`. + + Parameters + ---------- + article_id : int + The unique identifier for the article which is given by Intercom. + + title : typing.Optional[str] + The title of the article.For multilingual articles, this will be the title of the default language's content. + + description : typing.Optional[str] + The description of the article. For multilingual articles, this will be the description of the default language's content. + + body : typing.Optional[str] + The content of the article. For multilingual articles, this will be the body of the default language's content. + + author_id : typing.Optional[int] + The id of the author of the article. For multilingual articles, this will be the id of the author of the default language's content. Must be a teammate on the help center's workspace. + + state : typing.Optional[UpdateArticleRequestState] + Whether the article will be `published` or will be a `draft`. Defaults to draft. For multilingual articles, this will be the state of the default language's content. + + parent_id : typing.Optional[str] + The id of the article's parent collection or section. An article without this field stands alone. + + parent_type : typing.Optional[str] + The type of parent, which can either be a `collection` or `section`. + + translated_content : typing.Optional[ArticleTranslatedContent] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Article] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"articles/{jsonable_encoder(article_id)}", + method="PUT", + json={ + "title": title, + "description": description, + "body": body, + "author_id": author_id, + "state": state, + "parent_id": parent_id, + "parent_type": parent_type, + "translated_content": convert_and_respect_annotation_metadata( + object_=translated_content, annotation=ArticleTranslatedContent, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Article, + construct_type( + type_=Article, # 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 delete( + self, article_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DeletedArticleObject]: + """ + You can delete a single article by making a DELETE request to `https://api.intercom.io/articles/`. + + Parameters + ---------- + article_id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeletedArticleObject] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"articles/{jsonable_encoder(article_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeletedArticleObject, + construct_type( + type_=DeletedArticleObject, # 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 search( + self, + *, + phrase: typing.Optional[str] = None, + state: typing.Optional[str] = None, + help_center_id: typing.Optional[int] = None, + highlight: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ArticleSearchResponse]: + """ + You can search for articles by making a GET request to `https://api.intercom.io/articles/search`. + + Parameters + ---------- + phrase : typing.Optional[str] + The phrase within your articles to search for. + + state : typing.Optional[str] + The state of the Articles returned. One of `published`, `draft` or `all`. + + help_center_id : typing.Optional[int] + The ID of the Help Center to search in. + + highlight : typing.Optional[bool] + Return a highlighted version of the matching content within your articles. Refer to the response schema for more details. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ArticleSearchResponse] + Search successful + """ + _response = await self._client_wrapper.httpx_client.request( + "articles/search", + method="GET", + params={ + "phrase": phrase, + "state": state, + "help_center_id": help_center_id, + "highlight": highlight, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ArticleSearchResponse, + construct_type( + type_=ArticleSearchResponse, # 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) diff --git a/src/intercom/articles/types/__init__.py b/src/intercom/articles/types/__init__.py new file mode 100644 index 00000000..9df96ca2 --- /dev/null +++ b/src/intercom/articles/types/__init__.py @@ -0,0 +1,75 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .article import Article + from .article_list_item import ArticleListItem + from .article_list_item_state import ArticleListItemState + from .article_search_highlights import ArticleSearchHighlights + from .article_search_highlights_highlighted_summary_item_item import ( + ArticleSearchHighlightsHighlightedSummaryItemItem, + ) + from .article_search_highlights_highlighted_summary_item_item_type import ( + ArticleSearchHighlightsHighlightedSummaryItemItemType, + ) + from .article_search_highlights_highlighted_title_item import ArticleSearchHighlightsHighlightedTitleItem + from .article_search_highlights_highlighted_title_item_type import ArticleSearchHighlightsHighlightedTitleItemType + from .article_search_response import ArticleSearchResponse + from .article_search_response_data import ArticleSearchResponseData + from .internal_article import InternalArticle + from .update_article_request_state import UpdateArticleRequestState +_dynamic_imports: typing.Dict[str, str] = { + "Article": ".article", + "ArticleListItem": ".article_list_item", + "ArticleListItemState": ".article_list_item_state", + "ArticleSearchHighlights": ".article_search_highlights", + "ArticleSearchHighlightsHighlightedSummaryItemItem": ".article_search_highlights_highlighted_summary_item_item", + "ArticleSearchHighlightsHighlightedSummaryItemItemType": ".article_search_highlights_highlighted_summary_item_item_type", + "ArticleSearchHighlightsHighlightedTitleItem": ".article_search_highlights_highlighted_title_item", + "ArticleSearchHighlightsHighlightedTitleItemType": ".article_search_highlights_highlighted_title_item_type", + "ArticleSearchResponse": ".article_search_response", + "ArticleSearchResponseData": ".article_search_response_data", + "InternalArticle": ".internal_article", + "UpdateArticleRequestState": ".update_article_request_state", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "Article", + "ArticleListItem", + "ArticleListItemState", + "ArticleSearchHighlights", + "ArticleSearchHighlightsHighlightedSummaryItemItem", + "ArticleSearchHighlightsHighlightedSummaryItemItemType", + "ArticleSearchHighlightsHighlightedTitleItem", + "ArticleSearchHighlightsHighlightedTitleItemType", + "ArticleSearchResponse", + "ArticleSearchResponseData", + "InternalArticle", + "UpdateArticleRequestState", +] diff --git a/src/intercom/articles/types/article.py b/src/intercom/articles/types/article.py new file mode 100644 index 00000000..b5a10ed5 --- /dev/null +++ b/src/intercom/articles/types/article.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...types.article_statistics import ArticleStatistics +from .article_list_item import ArticleListItem + + +class Article(ArticleListItem): + """ + The Articles API is a central place to gather all information and take actions on your articles. Articles can live within collections and sections, or alternatively they can stand alone. + """ + + statistics: typing.Optional[ArticleStatistics] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/articles/types/article_list_item.py b/src/intercom/articles/types/article_list_item.py new file mode 100644 index 00000000..636a6caa --- /dev/null +++ b/src/intercom/articles/types/article_list_item.py @@ -0,0 +1,101 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.article_translated_content import ArticleTranslatedContent +from .article_list_item_state import ArticleListItemState + + +class ArticleListItem(UncheckedBaseModel): + """ + The data returned about your articles when you list them. + """ + + type: typing.Optional[typing.Literal["article"]] = pydantic.Field(default=None) + """ + The type of object - `article`. + """ + + id: str = pydantic.Field() + """ + The unique identifier for the article which is given by Intercom. + """ + + workspace_id: str = pydantic.Field() + """ + The id of the workspace which the article belongs to. + """ + + title: str = pydantic.Field() + """ + The title of the article. For multilingual articles, this will be the title of the default language's content. + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + The description of the article. For multilingual articles, this will be the description of the default language's content. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The body of the article in HTML. For multilingual articles, this will be the body of the default language's content. + """ + + author_id: int = pydantic.Field() + """ + The id of the author of the article. For multilingual articles, this will be the id of the author of the default language's content. Must be a teammate on the help center's workspace. + """ + + state: ArticleListItemState = pydantic.Field() + """ + Whether the article is `published` or is a `draft`. For multilingual articles, this will be the state of the default language's content. + """ + + created_at: int = pydantic.Field() + """ + The time when the article was created. For multilingual articles, this will be the timestamp of creation of the default language's content in seconds. + """ + + updated_at: int = pydantic.Field() + """ + The time when the article was last updated. For multilingual articles, this will be the timestamp of last update of the default language's content in seconds. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL of the article. For multilingual articles, this will be the URL of the default language's content. + """ + + parent_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of the article's parent collection or section. An article without this field stands alone. + """ + + parent_ids: typing.Optional[typing.List[int]] = pydantic.Field(default=None) + """ + The ids of the article's parent collections or sections. An article without this field stands alone. + """ + + parent_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of parent, which can either be a `collection` or `section`. + """ + + default_locale: typing.Optional[str] = pydantic.Field(default=None) + """ + The default locale of the help center. This field is only returned for multilingual help centers. + """ + + translated_content: typing.Optional[ArticleTranslatedContent] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/articles/types/article_list_item_state.py b/src/intercom/articles/types/article_list_item_state.py new file mode 100644 index 00000000..013499d9 --- /dev/null +++ b/src/intercom/articles/types/article_list_item_state.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ArticleListItemState = typing.Union[typing.Literal["published", "draft"], typing.Any] diff --git a/src/intercom/articles/types/article_search_highlights.py b/src/intercom/articles/types/article_search_highlights.py new file mode 100644 index 00000000..ca7efd5f --- /dev/null +++ b/src/intercom/articles/types/article_search_highlights.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .article_search_highlights_highlighted_summary_item_item import ArticleSearchHighlightsHighlightedSummaryItemItem +from .article_search_highlights_highlighted_title_item import ArticleSearchHighlightsHighlightedTitleItem + + +class ArticleSearchHighlights(UncheckedBaseModel): + """ + The highlighted results of an Article search. In the examples provided my search query is always "my query". + """ + + article_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the corresponding article. + """ + + highlighted_title: typing.Optional[typing.List[ArticleSearchHighlightsHighlightedTitleItem]] = pydantic.Field( + default=None + ) + """ + An Article title highlighted. + """ + + highlighted_summary: typing.Optional[ + typing.List[typing.List[ArticleSearchHighlightsHighlightedSummaryItemItem]] + ] = pydantic.Field(default=None) + """ + An Article description and body text highlighted. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/articles/types/article_search_highlights_highlighted_summary_item_item.py b/src/intercom/articles/types/article_search_highlights_highlighted_summary_item_item.py new file mode 100644 index 00000000..2e1a055e --- /dev/null +++ b/src/intercom/articles/types/article_search_highlights_highlighted_summary_item_item.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .article_search_highlights_highlighted_summary_item_item_type import ( + ArticleSearchHighlightsHighlightedSummaryItemItemType, +) + + +class ArticleSearchHighlightsHighlightedSummaryItemItem(UncheckedBaseModel): + """ + An instance of highlighted summary text. + """ + + type: typing.Optional[ArticleSearchHighlightsHighlightedSummaryItemItemType] = pydantic.Field(default=None) + """ + The type of text - `highlight` or `plain`. + """ + + text: typing.Optional[str] = pydantic.Field(default=None) + """ + The text of the title. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/articles/types/article_search_highlights_highlighted_summary_item_item_type.py b/src/intercom/articles/types/article_search_highlights_highlighted_summary_item_item_type.py new file mode 100644 index 00000000..295a1ec2 --- /dev/null +++ b/src/intercom/articles/types/article_search_highlights_highlighted_summary_item_item_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ArticleSearchHighlightsHighlightedSummaryItemItemType = typing.Union[typing.Literal["highlight", "plain"], typing.Any] diff --git a/src/intercom/articles/types/article_search_highlights_highlighted_title_item.py b/src/intercom/articles/types/article_search_highlights_highlighted_title_item.py new file mode 100644 index 00000000..8ca588c7 --- /dev/null +++ b/src/intercom/articles/types/article_search_highlights_highlighted_title_item.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .article_search_highlights_highlighted_title_item_type import ArticleSearchHighlightsHighlightedTitleItemType + + +class ArticleSearchHighlightsHighlightedTitleItem(UncheckedBaseModel): + """ + A highlighted article title. + """ + + type: typing.Optional[ArticleSearchHighlightsHighlightedTitleItemType] = pydantic.Field(default=None) + """ + The type of text - `highlight` or `plain`. + """ + + text: typing.Optional[str] = pydantic.Field(default=None) + """ + The text of the title. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/articles/types/article_search_highlights_highlighted_title_item_type.py b/src/intercom/articles/types/article_search_highlights_highlighted_title_item_type.py new file mode 100644 index 00000000..45d4316b --- /dev/null +++ b/src/intercom/articles/types/article_search_highlights_highlighted_title_item_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ArticleSearchHighlightsHighlightedTitleItemType = typing.Union[typing.Literal["highlight", "plain"], typing.Any] diff --git a/src/intercom/articles/types/article_search_response.py b/src/intercom/articles/types/article_search_response.py new file mode 100644 index 00000000..5c540b3d --- /dev/null +++ b/src/intercom/articles/types/article_search_response.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.cursor_pages import CursorPages +from .article_search_response_data import ArticleSearchResponseData + + +class ArticleSearchResponse(UncheckedBaseModel): + """ + The results of an Article search + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object - `list`. + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + The total number of Articles matching the search query + """ + + data: typing.Optional[ArticleSearchResponseData] = pydantic.Field(default=None) + """ + An object containing the results of the search. + """ + + pages: typing.Optional[CursorPages] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/articles/types/article_search_response_data.py b/src/intercom/articles/types/article_search_response_data.py new file mode 100644 index 00000000..741e1a7e --- /dev/null +++ b/src/intercom/articles/types/article_search_response_data.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .article import Article +from .article_search_highlights import ArticleSearchHighlights + + +class ArticleSearchResponseData(UncheckedBaseModel): + """ + An object containing the results of the search. + """ + + articles: typing.Optional[typing.List[Article]] = pydantic.Field(default=None) + """ + An array of Article objects + """ + + highlights: typing.Optional[typing.List[ArticleSearchHighlights]] = pydantic.Field(default=None) + """ + A corresponding array of highlighted Article content + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/articles/types/internal_article.py b/src/intercom/articles/types/internal_article.py new file mode 100644 index 00000000..3b83a547 --- /dev/null +++ b/src/intercom/articles/types/internal_article.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +from ...internal_articles.types.internal_article_list_item import InternalArticleListItem + +InternalArticle = InternalArticleListItem diff --git a/src/intercom/articles/types/update_article_request_state.py b/src/intercom/articles/types/update_article_request_state.py new file mode 100644 index 00000000..0e220b91 --- /dev/null +++ b/src/intercom/articles/types/update_article_request_state.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +UpdateArticleRequestState = typing.Union[typing.Literal["published", "draft"], typing.Any] diff --git a/src/intercom/away_status_reasons/__init__.py b/src/intercom/away_status_reasons/__init__.py new file mode 100644 index 00000000..5cde0202 --- /dev/null +++ b/src/intercom/away_status_reasons/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/src/intercom/away_status_reasons/client.py b/src/intercom/away_status_reasons/client.py new file mode 100644 index 00000000..b8971cad --- /dev/null +++ b/src/intercom/away_status_reasons/client.py @@ -0,0 +1,104 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.away_status_reason import AwayStatusReason +from .raw_client import AsyncRawAwayStatusReasonsClient, RawAwayStatusReasonsClient + + +class AwayStatusReasonsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawAwayStatusReasonsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawAwayStatusReasonsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawAwayStatusReasonsClient + """ + return self._raw_client + + def list_away_status_reasons( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.List[AwayStatusReason]: + """ + Returns a list of all away status reasons configured for the workspace, including deleted ones. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.List[AwayStatusReason] + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.away_status_reasons.list_away_status_reasons() + """ + _response = self._raw_client.list_away_status_reasons(request_options=request_options) + return _response.data + + +class AsyncAwayStatusReasonsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawAwayStatusReasonsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawAwayStatusReasonsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawAwayStatusReasonsClient + """ + return self._raw_client + + async def list_away_status_reasons( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.List[AwayStatusReason]: + """ + Returns a list of all away status reasons configured for the workspace, including deleted ones. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.List[AwayStatusReason] + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.away_status_reasons.list_away_status_reasons() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_away_status_reasons(request_options=request_options) + return _response.data diff --git a/src/intercom/away_status_reasons/raw_client.py b/src/intercom/away_status_reasons/raw_client.py new file mode 100644 index 00000000..62fbd5c3 --- /dev/null +++ b/src/intercom/away_status_reasons/raw_client.py @@ -0,0 +1,117 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.request_options import RequestOptions +from ..core.unchecked_base_model import construct_type +from ..errors.unauthorized_error import UnauthorizedError +from ..types.away_status_reason import AwayStatusReason +from ..types.error import Error + + +class RawAwayStatusReasonsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_away_status_reasons( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[typing.List[AwayStatusReason]]: + """ + Returns a list of all away status reasons configured for the workspace, including deleted ones. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.List[AwayStatusReason]] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "away_status_reasons", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.List[AwayStatusReason], + construct_type( + type_=typing.List[AwayStatusReason], # 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) + + +class AsyncRawAwayStatusReasonsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_away_status_reasons( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[typing.List[AwayStatusReason]]: + """ + Returns a list of all away status reasons configured for the workspace, including deleted ones. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.List[AwayStatusReason]] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "away_status_reasons", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.List[AwayStatusReason], + construct_type( + type_=typing.List[AwayStatusReason], # 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) diff --git a/src/intercom/calls/__init__.py b/src/intercom/calls/__init__.py new file mode 100644 index 00000000..c3418558 --- /dev/null +++ b/src/intercom/calls/__init__.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import Call, ListCallsWithTranscriptsResponse, ListCallsWithTranscriptsResponseDataItem +_dynamic_imports: typing.Dict[str, str] = { + "Call": ".types", + "ListCallsWithTranscriptsResponse": ".types", + "ListCallsWithTranscriptsResponseDataItem": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Call", "ListCallsWithTranscriptsResponse", "ListCallsWithTranscriptsResponseDataItem"] diff --git a/src/intercom/calls/client.py b/src/intercom/calls/client.py new file mode 100644 index 00000000..533dbf50 --- /dev/null +++ b/src/intercom/calls/client.py @@ -0,0 +1,427 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.call_list import CallList +from .raw_client import AsyncRawCallsClient, RawCallsClient +from .types.call import Call +from .types.list_calls_with_transcripts_response import ListCallsWithTranscriptsResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class CallsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawCallsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawCallsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawCallsClient + """ + return self._raw_client + + def list_calls( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> CallList: + """ + Retrieve a paginated list of calls. + + 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 25. Max 25. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CallList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.calls.list_calls( + page=1, + per_page=1, + ) + """ + _response = self._raw_client.list_calls(page=page, per_page=per_page, request_options=request_options) + return _response.data + + def show_call(self, call_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Call: + """ + Retrieve a single call by id. + + Parameters + ---------- + call_id : str + The id of the call to retrieve + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Call + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.calls.show_call( + call_id="call_id", + ) + """ + _response = self._raw_client.show_call(call_id, request_options=request_options) + return _response.data + + def show_call_recording(self, call_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + Redirects to a signed URL for the call's recording if it exists. + + Parameters + ---------- + call_id : str + The id of the call + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.calls.show_call_recording( + call_id="call_id", + ) + """ + _response = self._raw_client.show_call_recording(call_id, request_options=request_options) + return _response.data + + def show_call_transcript(self, call_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> str: + """ + Returns the transcript for the specified call as a downloadable text file. + + Parameters + ---------- + call_id : str + The id of the call + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + str + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.calls.show_call_transcript( + call_id="call_id", + ) + """ + _response = self._raw_client.show_call_transcript(call_id, request_options=request_options) + return _response.data + + def list_calls_with_transcripts( + self, *, conversation_ids: typing.Sequence[str], request_options: typing.Optional[RequestOptions] = None + ) -> ListCallsWithTranscriptsResponse: + """ + Retrieve calls by a list of conversation ids and include transcripts when available. + A maximum of 20 `conversation_ids` can be provided. If none are provided or more than 20 are provided, a 400 error is returned. + + Parameters + ---------- + conversation_ids : typing.Sequence[str] + A list of conversation ids to fetch calls for. Maximum 20. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ListCallsWithTranscriptsResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.calls.list_calls_with_transcripts( + conversation_ids=["64619700005694", "64619700005695"], + ) + """ + _response = self._raw_client.list_calls_with_transcripts( + conversation_ids=conversation_ids, request_options=request_options + ) + return _response.data + + +class AsyncCallsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawCallsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawCallsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawCallsClient + """ + return self._raw_client + + async def list_calls( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> CallList: + """ + Retrieve a paginated list of calls. + + 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 25. Max 25. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CallList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.calls.list_calls( + page=1, + per_page=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_calls(page=page, per_page=per_page, request_options=request_options) + return _response.data + + async def show_call(self, call_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Call: + """ + Retrieve a single call by id. + + Parameters + ---------- + call_id : str + The id of the call to retrieve + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Call + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.calls.show_call( + call_id="call_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.show_call(call_id, request_options=request_options) + return _response.data + + async def show_call_recording( + self, call_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + Redirects to a signed URL for the call's recording if it exists. + + Parameters + ---------- + call_id : str + The id of the call + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.calls.show_call_recording( + call_id="call_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.show_call_recording(call_id, request_options=request_options) + return _response.data + + async def show_call_transcript( + self, call_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> str: + """ + Returns the transcript for the specified call as a downloadable text file. + + Parameters + ---------- + call_id : str + The id of the call + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + str + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.calls.show_call_transcript( + call_id="call_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.show_call_transcript(call_id, request_options=request_options) + return _response.data + + async def list_calls_with_transcripts( + self, *, conversation_ids: typing.Sequence[str], request_options: typing.Optional[RequestOptions] = None + ) -> ListCallsWithTranscriptsResponse: + """ + Retrieve calls by a list of conversation ids and include transcripts when available. + A maximum of 20 `conversation_ids` can be provided. If none are provided or more than 20 are provided, a 400 error is returned. + + Parameters + ---------- + conversation_ids : typing.Sequence[str] + A list of conversation ids to fetch calls for. Maximum 20. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ListCallsWithTranscriptsResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.calls.list_calls_with_transcripts( + conversation_ids=["64619700005694", "64619700005695"], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_calls_with_transcripts( + conversation_ids=conversation_ids, request_options=request_options + ) + return _response.data diff --git a/src/intercom/calls/raw_client.py b/src/intercom/calls/raw_client.py new file mode 100644 index 00000000..784083ab --- /dev/null +++ b/src/intercom/calls/raw_client.py @@ -0,0 +1,603 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ..core.unchecked_base_model import construct_type +from ..errors.bad_request_error import BadRequestError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.call_list import CallList +from ..types.error import Error +from .types.call import Call +from .types.list_calls_with_transcripts_response import ListCallsWithTranscriptsResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawCallsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_calls( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[CallList]: + """ + Retrieve a paginated list of calls. + + 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 25. Max 25. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CallList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "calls", + method="GET", + params={ + "page": page, + "per_page": per_page, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CallList, + construct_type( + type_=CallList, # 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_call(self, call_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Call]: + """ + Retrieve a single call by id. + + Parameters + ---------- + call_id : str + The id of the call to retrieve + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Call] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"calls/{jsonable_encoder(call_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Call, + construct_type( + type_=Call, # 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 show_call_recording( + self, call_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[None]: + """ + Redirects to a signed URL for the call's recording if it exists. + + Parameters + ---------- + call_id : str + The id of the call + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + f"calls/{jsonable_encoder(call_id)}/recording", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + 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 show_call_transcript( + self, call_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[str]: + """ + Returns the transcript for the specified call as a downloadable text file. + + Parameters + ---------- + call_id : str + The id of the call + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[str] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"calls/{jsonable_encoder(call_id)}/transcript", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=_response.text) # type: ignore + 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_calls_with_transcripts( + self, *, conversation_ids: typing.Sequence[str], request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ListCallsWithTranscriptsResponse]: + """ + Retrieve calls by a list of conversation ids and include transcripts when available. + A maximum of 20 `conversation_ids` can be provided. If none are provided or more than 20 are provided, a 400 error is returned. + + Parameters + ---------- + conversation_ids : typing.Sequence[str] + A list of conversation ids to fetch calls for. Maximum 20. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ListCallsWithTranscriptsResponse] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "calls/search", + method="POST", + json={ + "conversation_ids": conversation_ids, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ListCallsWithTranscriptsResponse, + construct_type( + type_=ListCallsWithTranscriptsResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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) + + +class AsyncRawCallsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_calls( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[CallList]: + """ + Retrieve a paginated list of calls. + + 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 25. Max 25. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CallList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "calls", + method="GET", + params={ + "page": page, + "per_page": per_page, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CallList, + construct_type( + type_=CallList, # 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_call( + self, call_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Call]: + """ + Retrieve a single call by id. + + Parameters + ---------- + call_id : str + The id of the call to retrieve + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Call] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"calls/{jsonable_encoder(call_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Call, + construct_type( + type_=Call, # 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 show_call_recording( + self, call_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + Redirects to a signed URL for the call's recording if it exists. + + Parameters + ---------- + call_id : str + The id of the call + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + f"calls/{jsonable_encoder(call_id)}/recording", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + 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 show_call_transcript( + self, call_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[str]: + """ + Returns the transcript for the specified call as a downloadable text file. + + Parameters + ---------- + call_id : str + The id of the call + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[str] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"calls/{jsonable_encoder(call_id)}/transcript", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=_response.text) # type: ignore + 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_calls_with_transcripts( + self, *, conversation_ids: typing.Sequence[str], request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ListCallsWithTranscriptsResponse]: + """ + Retrieve calls by a list of conversation ids and include transcripts when available. + A maximum of 20 `conversation_ids` can be provided. If none are provided or more than 20 are provided, a 400 error is returned. + + Parameters + ---------- + conversation_ids : typing.Sequence[str] + A list of conversation ids to fetch calls for. Maximum 20. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ListCallsWithTranscriptsResponse] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "calls/search", + method="POST", + json={ + "conversation_ids": conversation_ids, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ListCallsWithTranscriptsResponse, + construct_type( + type_=ListCallsWithTranscriptsResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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) diff --git a/src/intercom/calls/types/__init__.py b/src/intercom/calls/types/__init__.py new file mode 100644 index 00000000..8337976c --- /dev/null +++ b/src/intercom/calls/types/__init__.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .call import Call + from .list_calls_with_transcripts_response import ListCallsWithTranscriptsResponse + from .list_calls_with_transcripts_response_data_item import ListCallsWithTranscriptsResponseDataItem +_dynamic_imports: typing.Dict[str, str] = { + "Call": ".call", + "ListCallsWithTranscriptsResponse": ".list_calls_with_transcripts_response", + "ListCallsWithTranscriptsResponseDataItem": ".list_calls_with_transcripts_response_data_item", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Call", "ListCallsWithTranscriptsResponse", "ListCallsWithTranscriptsResponseDataItem"] diff --git a/src/intercom/calls/types/call.py b/src/intercom/calls/types/call.py new file mode 100644 index 00000000..96d64341 --- /dev/null +++ b/src/intercom/calls/types/call.py @@ -0,0 +1,98 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.datetime import Datetime + + +class Call(UncheckedBaseModel): + """ + Represents a phone call in Intercom + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `call`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the call. + """ + + conversation_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the conversation associated with the call, if any. + """ + + admin_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the admin associated with the call, if any. + """ + + contact_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the contact associated with the call, if any. + """ + + state: typing.Optional[str] = pydantic.Field(default=None) + """ + The current state of the call. + """ + + initiated_at: typing.Optional[Datetime] = None + answered_at: typing.Optional[Datetime] = None + ended_at: typing.Optional[Datetime] = None + created_at: typing.Optional[Datetime] = None + updated_at: typing.Optional[Datetime] = None + recording_url: typing.Optional[str] = pydantic.Field(default=None) + """ + API URL to download or redirect to the call recording if available. + """ + + transcription_url: typing.Optional[str] = pydantic.Field(default=None) + """ + API URL to download or redirect to the call transcript if available. + """ + + call_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of call. + """ + + direction: typing.Optional[str] = pydantic.Field(default=None) + """ + The direction of the call. + """ + + ended_reason: typing.Optional[str] = pydantic.Field(default=None) + """ + The reason for the call end, if applicable. + """ + + phone: typing.Optional[str] = pydantic.Field(default=None) + """ + The phone number involved in the call, in E.164 format. + """ + + fin_recording_url: typing.Optional[str] = pydantic.Field(default=None) + """ + API URL to the AI Agent (Fin) call recording if available. + """ + + fin_transcription_url: typing.Optional[str] = pydantic.Field(default=None) + """ + API URL to the AI Agent (Fin) call transcript if available. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/calls/types/list_calls_with_transcripts_response.py b/src/intercom/calls/types/list_calls_with_transcripts_response.py new file mode 100644 index 00000000..999c3129 --- /dev/null +++ b/src/intercom/calls/types/list_calls_with_transcripts_response.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .list_calls_with_transcripts_response_data_item import ListCallsWithTranscriptsResponseDataItem + + +class ListCallsWithTranscriptsResponse(UncheckedBaseModel): + type: typing.Optional[str] = None + data: typing.Optional[typing.List[ListCallsWithTranscriptsResponseDataItem]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/calls/types/list_calls_with_transcripts_response_data_item.py b/src/intercom/calls/types/list_calls_with_transcripts_response_data_item.py new file mode 100644 index 00000000..fdf853bb --- /dev/null +++ b/src/intercom/calls/types/list_calls_with_transcripts_response_data_item.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from .call import Call + + +class ListCallsWithTranscriptsResponseDataItem(Call): + transcript: typing.Optional[typing.List[typing.Dict[str, typing.Any]]] = pydantic.Field(default=None) + """ + The call transcript if available, otherwise an empty array. + """ + + transcript_status: typing.Optional[str] = pydantic.Field(default=None) + """ + The status of the transcript if available. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/client.py b/src/intercom/client.py new file mode 100644 index 00000000..775b98be --- /dev/null +++ b/src/intercom/client.py @@ -0,0 +1,734 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import os +import typing + +import httpx +from .core.api_error import ApiError +from .core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from .environment import IntercomEnvironment + +if typing.TYPE_CHECKING: + from .admins.client import AdminsClient, AsyncAdminsClient + from .ai_content.client import AiContentClient, AsyncAiContentClient + from .articles.client import ArticlesClient, AsyncArticlesClient + from .away_status_reasons.client import AsyncAwayStatusReasonsClient, AwayStatusReasonsClient + from .calls.client import AsyncCallsClient, CallsClient + from .companies.client import AsyncCompaniesClient, CompaniesClient + from .contacts.client import AsyncContactsClient, ContactsClient + from .conversations.client import AsyncConversationsClient, ConversationsClient + from .custom_channel_events.client import AsyncCustomChannelEventsClient, CustomChannelEventsClient + from .custom_object_instances.client import AsyncCustomObjectInstancesClient, CustomObjectInstancesClient + from .data_attributes.client import AsyncDataAttributesClient, DataAttributesClient + from .data_export.client import AsyncDataExportClient, DataExportClient + from .events.client import AsyncEventsClient, EventsClient + from .export.client import AsyncExportClient, ExportClient + from .help_centers.client import AsyncHelpCentersClient, HelpCentersClient + from .internal_articles.client import AsyncInternalArticlesClient, InternalArticlesClient + from .jobs.client import AsyncJobsClient, JobsClient + from .messages.client import AsyncMessagesClient, MessagesClient + from .news.client import AsyncNewsClient, NewsClient + from .notes.client import AsyncNotesClient, NotesClient + from .phone_call_redirects.client import AsyncPhoneCallRedirectsClient, PhoneCallRedirectsClient + from .segments.client import AsyncSegmentsClient, SegmentsClient + from .subscription_types.client import AsyncSubscriptionTypesClient, SubscriptionTypesClient + from .tags.client import AsyncTagsClient, TagsClient + from .teams.client import AsyncTeamsClient, TeamsClient + from .ticket_states.client import AsyncTicketStatesClient, TicketStatesClient + from .ticket_types.client import AsyncTicketTypesClient, TicketTypesClient + from .tickets.client import AsyncTicketsClient, TicketsClient + from .unstable.client import AsyncUnstableClient, UnstableClient + from .visitors.client import AsyncVisitorsClient, VisitorsClient + + +class Intercom: + """ + Use this class to access the different functions within the SDK. You can instantiate any number of clients with different configuration that will propagate to these functions. + + Parameters + ---------- + base_url : typing.Optional[str] + The base url to use for requests from the client. + + environment : IntercomEnvironment + The environment to use for requests from the client. from .environment import IntercomEnvironment + + + + Defaults to IntercomEnvironment.US_PRODUCTION + + + + token : typing.Optional[typing.Union[str, typing.Callable[[], str]]] + headers : typing.Optional[typing.Dict[str, str]] + Additional headers to send with every request. + + timeout : typing.Optional[float] + The timeout to be used, in seconds, for requests. By default the timeout is 60 seconds, unless a custom httpx client is used, in which case this default is not enforced. + + follow_redirects : typing.Optional[bool] + Whether the default httpx client follows redirects or not, this is irrelevant if a custom httpx client is passed in. + + httpx_client : typing.Optional[httpx.Client] + The httpx client to use for making requests, a preconfigured client is used by default, however this is useful should you want to pass in any custom httpx configuration. + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + """ + + def __init__( + self, + *, + base_url: typing.Optional[str] = None, + environment: IntercomEnvironment = IntercomEnvironment.US_PRODUCTION, + token: typing.Optional[typing.Union[str, typing.Callable[[], str]]] = os.getenv("INTERCOM_API_KEY"), + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.Client] = None, + ): + _defaulted_timeout = ( + timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read + ) + if token is None: + raise ApiError( + body="The client must be instantiated be either passing in token or setting INTERCOM_API_KEY" + ) + self._client_wrapper = SyncClientWrapper( + base_url=_get_base_url(base_url=base_url, environment=environment), + token=token, + headers=headers, + httpx_client=httpx_client + if httpx_client is not None + else httpx.Client(timeout=_defaulted_timeout, follow_redirects=follow_redirects) + if follow_redirects is not None + else httpx.Client(timeout=_defaulted_timeout), + timeout=_defaulted_timeout, + ) + self._admins: typing.Optional[AdminsClient] = None + self._ai_content: typing.Optional[AiContentClient] = None + self._articles: typing.Optional[ArticlesClient] = None + self._away_status_reasons: typing.Optional[AwayStatusReasonsClient] = None + self._export: typing.Optional[ExportClient] = None + self._data_export: typing.Optional[DataExportClient] = None + self._help_centers: typing.Optional[HelpCentersClient] = None + self._internal_articles: typing.Optional[InternalArticlesClient] = None + self._companies: typing.Optional[CompaniesClient] = None + self._contacts: typing.Optional[ContactsClient] = None + self._notes: typing.Optional[NotesClient] = None + self._tags: typing.Optional[TagsClient] = None + self._conversations: typing.Optional[ConversationsClient] = None + self._custom_channel_events: typing.Optional[CustomChannelEventsClient] = None + self._custom_object_instances: typing.Optional[CustomObjectInstancesClient] = None + self._data_attributes: typing.Optional[DataAttributesClient] = None + self._events: typing.Optional[EventsClient] = None + self._jobs: typing.Optional[JobsClient] = None + self._messages: typing.Optional[MessagesClient] = None + self._segments: typing.Optional[SegmentsClient] = None + self._subscription_types: typing.Optional[SubscriptionTypesClient] = None + self._phone_call_redirects: typing.Optional[PhoneCallRedirectsClient] = None + self._calls: typing.Optional[CallsClient] = None + self._teams: typing.Optional[TeamsClient] = None + self._ticket_states: typing.Optional[TicketStatesClient] = None + self._ticket_types: typing.Optional[TicketTypesClient] = None + self._tickets: typing.Optional[TicketsClient] = None + self._visitors: typing.Optional[VisitorsClient] = None + self._news: typing.Optional[NewsClient] = None + self._unstable: typing.Optional[UnstableClient] = None + + @property + def admins(self): + if self._admins is None: + from .admins.client import AdminsClient # noqa: E402 + + self._admins = AdminsClient(client_wrapper=self._client_wrapper) + return self._admins + + @property + def ai_content(self): + if self._ai_content is None: + from .ai_content.client import AiContentClient # noqa: E402 + + self._ai_content = AiContentClient(client_wrapper=self._client_wrapper) + return self._ai_content + + @property + def articles(self): + if self._articles is None: + from .articles.client import ArticlesClient # noqa: E402 + + self._articles = ArticlesClient(client_wrapper=self._client_wrapper) + return self._articles + + @property + def away_status_reasons(self): + if self._away_status_reasons is None: + from .away_status_reasons.client import AwayStatusReasonsClient # noqa: E402 + + self._away_status_reasons = AwayStatusReasonsClient(client_wrapper=self._client_wrapper) + return self._away_status_reasons + + @property + def export(self): + if self._export is None: + from .export.client import ExportClient # noqa: E402 + + self._export = ExportClient(client_wrapper=self._client_wrapper) + return self._export + + @property + def data_export(self): + if self._data_export is None: + from .data_export.client import DataExportClient # noqa: E402 + + self._data_export = DataExportClient(client_wrapper=self._client_wrapper) + return self._data_export + + @property + def help_centers(self): + if self._help_centers is None: + from .help_centers.client import HelpCentersClient # noqa: E402 + + self._help_centers = HelpCentersClient(client_wrapper=self._client_wrapper) + return self._help_centers + + @property + def internal_articles(self): + if self._internal_articles is None: + from .internal_articles.client import InternalArticlesClient # noqa: E402 + + self._internal_articles = InternalArticlesClient(client_wrapper=self._client_wrapper) + return self._internal_articles + + @property + def companies(self): + if self._companies is None: + from .companies.client import CompaniesClient # noqa: E402 + + self._companies = CompaniesClient(client_wrapper=self._client_wrapper) + return self._companies + + @property + def contacts(self): + if self._contacts is None: + from .contacts.client import ContactsClient # noqa: E402 + + self._contacts = ContactsClient(client_wrapper=self._client_wrapper) + return self._contacts + + @property + def notes(self): + if self._notes is None: + from .notes.client import NotesClient # noqa: E402 + + self._notes = NotesClient(client_wrapper=self._client_wrapper) + return self._notes + + @property + def tags(self): + if self._tags is None: + from .tags.client import TagsClient # noqa: E402 + + self._tags = TagsClient(client_wrapper=self._client_wrapper) + return self._tags + + @property + def conversations(self): + if self._conversations is None: + from .conversations.client import ConversationsClient # noqa: E402 + + self._conversations = ConversationsClient(client_wrapper=self._client_wrapper) + return self._conversations + + @property + def custom_channel_events(self): + if self._custom_channel_events is None: + from .custom_channel_events.client import CustomChannelEventsClient # noqa: E402 + + self._custom_channel_events = CustomChannelEventsClient(client_wrapper=self._client_wrapper) + return self._custom_channel_events + + @property + def custom_object_instances(self): + if self._custom_object_instances is None: + from .custom_object_instances.client import CustomObjectInstancesClient # noqa: E402 + + self._custom_object_instances = CustomObjectInstancesClient(client_wrapper=self._client_wrapper) + return self._custom_object_instances + + @property + def data_attributes(self): + if self._data_attributes is None: + from .data_attributes.client import DataAttributesClient # noqa: E402 + + self._data_attributes = DataAttributesClient(client_wrapper=self._client_wrapper) + return self._data_attributes + + @property + def events(self): + if self._events is None: + from .events.client import EventsClient # noqa: E402 + + self._events = EventsClient(client_wrapper=self._client_wrapper) + return self._events + + @property + def jobs(self): + if self._jobs is None: + from .jobs.client import JobsClient # noqa: E402 + + self._jobs = JobsClient(client_wrapper=self._client_wrapper) + return self._jobs + + @property + def messages(self): + if self._messages is None: + from .messages.client import MessagesClient # noqa: E402 + + self._messages = MessagesClient(client_wrapper=self._client_wrapper) + return self._messages + + @property + def segments(self): + if self._segments is None: + from .segments.client import SegmentsClient # noqa: E402 + + self._segments = SegmentsClient(client_wrapper=self._client_wrapper) + return self._segments + + @property + def subscription_types(self): + if self._subscription_types is None: + from .subscription_types.client import SubscriptionTypesClient # noqa: E402 + + self._subscription_types = SubscriptionTypesClient(client_wrapper=self._client_wrapper) + return self._subscription_types + + @property + def phone_call_redirects(self): + if self._phone_call_redirects is None: + from .phone_call_redirects.client import PhoneCallRedirectsClient # noqa: E402 + + self._phone_call_redirects = PhoneCallRedirectsClient(client_wrapper=self._client_wrapper) + return self._phone_call_redirects + + @property + def calls(self): + if self._calls is None: + from .calls.client import CallsClient # noqa: E402 + + self._calls = CallsClient(client_wrapper=self._client_wrapper) + return self._calls + + @property + def teams(self): + if self._teams is None: + from .teams.client import TeamsClient # noqa: E402 + + self._teams = TeamsClient(client_wrapper=self._client_wrapper) + return self._teams + + @property + def ticket_states(self): + if self._ticket_states is None: + from .ticket_states.client import TicketStatesClient # noqa: E402 + + self._ticket_states = TicketStatesClient(client_wrapper=self._client_wrapper) + return self._ticket_states + + @property + def ticket_types(self): + if self._ticket_types is None: + from .ticket_types.client import TicketTypesClient # noqa: E402 + + self._ticket_types = TicketTypesClient(client_wrapper=self._client_wrapper) + return self._ticket_types + + @property + def tickets(self): + if self._tickets is None: + from .tickets.client import TicketsClient # noqa: E402 + + self._tickets = TicketsClient(client_wrapper=self._client_wrapper) + return self._tickets + + @property + def visitors(self): + if self._visitors is None: + from .visitors.client import VisitorsClient # noqa: E402 + + self._visitors = VisitorsClient(client_wrapper=self._client_wrapper) + return self._visitors + + @property + def news(self): + if self._news is None: + from .news.client import NewsClient # noqa: E402 + + self._news = NewsClient(client_wrapper=self._client_wrapper) + return self._news + + @property + def unstable(self): + if self._unstable is None: + from .unstable.client import UnstableClient # noqa: E402 + + self._unstable = UnstableClient(client_wrapper=self._client_wrapper) + return self._unstable + + +class AsyncIntercom: + """ + Use this class to access the different functions within the SDK. You can instantiate any number of clients with different configuration that will propagate to these functions. + + Parameters + ---------- + base_url : typing.Optional[str] + The base url to use for requests from the client. + + environment : IntercomEnvironment + The environment to use for requests from the client. from .environment import IntercomEnvironment + + + + Defaults to IntercomEnvironment.US_PRODUCTION + + + + token : typing.Optional[typing.Union[str, typing.Callable[[], str]]] + headers : typing.Optional[typing.Dict[str, str]] + Additional headers to send with every request. + + timeout : typing.Optional[float] + The timeout to be used, in seconds, for requests. By default the timeout is 60 seconds, unless a custom httpx client is used, in which case this default is not enforced. + + follow_redirects : typing.Optional[bool] + Whether the default httpx client follows redirects or not, this is irrelevant if a custom httpx client is passed in. + + httpx_client : typing.Optional[httpx.AsyncClient] + The httpx client to use for making requests, a preconfigured client is used by default, however this is useful should you want to pass in any custom httpx configuration. + + Examples + -------- + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + """ + + def __init__( + self, + *, + base_url: typing.Optional[str] = None, + environment: IntercomEnvironment = IntercomEnvironment.US_PRODUCTION, + token: typing.Optional[typing.Union[str, typing.Callable[[], str]]] = os.getenv("INTERCOM_API_KEY"), + headers: typing.Optional[typing.Dict[str, str]] = None, + timeout: typing.Optional[float] = None, + follow_redirects: typing.Optional[bool] = True, + httpx_client: typing.Optional[httpx.AsyncClient] = None, + ): + _defaulted_timeout = ( + timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read + ) + if token is None: + raise ApiError( + body="The client must be instantiated be either passing in token or setting INTERCOM_API_KEY" + ) + self._client_wrapper = AsyncClientWrapper( + base_url=_get_base_url(base_url=base_url, environment=environment), + token=token, + headers=headers, + httpx_client=httpx_client + if httpx_client is not None + else httpx.AsyncClient(timeout=_defaulted_timeout, follow_redirects=follow_redirects) + if follow_redirects is not None + else httpx.AsyncClient(timeout=_defaulted_timeout), + timeout=_defaulted_timeout, + ) + self._admins: typing.Optional[AsyncAdminsClient] = None + self._ai_content: typing.Optional[AsyncAiContentClient] = None + self._articles: typing.Optional[AsyncArticlesClient] = None + self._away_status_reasons: typing.Optional[AsyncAwayStatusReasonsClient] = None + self._export: typing.Optional[AsyncExportClient] = None + self._data_export: typing.Optional[AsyncDataExportClient] = None + self._help_centers: typing.Optional[AsyncHelpCentersClient] = None + self._internal_articles: typing.Optional[AsyncInternalArticlesClient] = None + self._companies: typing.Optional[AsyncCompaniesClient] = None + self._contacts: typing.Optional[AsyncContactsClient] = None + self._notes: typing.Optional[AsyncNotesClient] = None + self._tags: typing.Optional[AsyncTagsClient] = None + self._conversations: typing.Optional[AsyncConversationsClient] = None + self._custom_channel_events: typing.Optional[AsyncCustomChannelEventsClient] = None + self._custom_object_instances: typing.Optional[AsyncCustomObjectInstancesClient] = None + self._data_attributes: typing.Optional[AsyncDataAttributesClient] = None + self._events: typing.Optional[AsyncEventsClient] = None + self._jobs: typing.Optional[AsyncJobsClient] = None + self._messages: typing.Optional[AsyncMessagesClient] = None + self._segments: typing.Optional[AsyncSegmentsClient] = None + self._subscription_types: typing.Optional[AsyncSubscriptionTypesClient] = None + self._phone_call_redirects: typing.Optional[AsyncPhoneCallRedirectsClient] = None + self._calls: typing.Optional[AsyncCallsClient] = None + self._teams: typing.Optional[AsyncTeamsClient] = None + self._ticket_states: typing.Optional[AsyncTicketStatesClient] = None + self._ticket_types: typing.Optional[AsyncTicketTypesClient] = None + self._tickets: typing.Optional[AsyncTicketsClient] = None + self._visitors: typing.Optional[AsyncVisitorsClient] = None + self._news: typing.Optional[AsyncNewsClient] = None + self._unstable: typing.Optional[AsyncUnstableClient] = None + + @property + def admins(self): + if self._admins is None: + from .admins.client import AsyncAdminsClient # noqa: E402 + + self._admins = AsyncAdminsClient(client_wrapper=self._client_wrapper) + return self._admins + + @property + def ai_content(self): + if self._ai_content is None: + from .ai_content.client import AsyncAiContentClient # noqa: E402 + + self._ai_content = AsyncAiContentClient(client_wrapper=self._client_wrapper) + return self._ai_content + + @property + def articles(self): + if self._articles is None: + from .articles.client import AsyncArticlesClient # noqa: E402 + + self._articles = AsyncArticlesClient(client_wrapper=self._client_wrapper) + return self._articles + + @property + def away_status_reasons(self): + if self._away_status_reasons is None: + from .away_status_reasons.client import AsyncAwayStatusReasonsClient # noqa: E402 + + self._away_status_reasons = AsyncAwayStatusReasonsClient(client_wrapper=self._client_wrapper) + return self._away_status_reasons + + @property + def export(self): + if self._export is None: + from .export.client import AsyncExportClient # noqa: E402 + + self._export = AsyncExportClient(client_wrapper=self._client_wrapper) + return self._export + + @property + def data_export(self): + if self._data_export is None: + from .data_export.client import AsyncDataExportClient # noqa: E402 + + self._data_export = AsyncDataExportClient(client_wrapper=self._client_wrapper) + return self._data_export + + @property + def help_centers(self): + if self._help_centers is None: + from .help_centers.client import AsyncHelpCentersClient # noqa: E402 + + self._help_centers = AsyncHelpCentersClient(client_wrapper=self._client_wrapper) + return self._help_centers + + @property + def internal_articles(self): + if self._internal_articles is None: + from .internal_articles.client import AsyncInternalArticlesClient # noqa: E402 + + self._internal_articles = AsyncInternalArticlesClient(client_wrapper=self._client_wrapper) + return self._internal_articles + + @property + def companies(self): + if self._companies is None: + from .companies.client import AsyncCompaniesClient # noqa: E402 + + self._companies = AsyncCompaniesClient(client_wrapper=self._client_wrapper) + return self._companies + + @property + def contacts(self): + if self._contacts is None: + from .contacts.client import AsyncContactsClient # noqa: E402 + + self._contacts = AsyncContactsClient(client_wrapper=self._client_wrapper) + return self._contacts + + @property + def notes(self): + if self._notes is None: + from .notes.client import AsyncNotesClient # noqa: E402 + + self._notes = AsyncNotesClient(client_wrapper=self._client_wrapper) + return self._notes + + @property + def tags(self): + if self._tags is None: + from .tags.client import AsyncTagsClient # noqa: E402 + + self._tags = AsyncTagsClient(client_wrapper=self._client_wrapper) + return self._tags + + @property + def conversations(self): + if self._conversations is None: + from .conversations.client import AsyncConversationsClient # noqa: E402 + + self._conversations = AsyncConversationsClient(client_wrapper=self._client_wrapper) + return self._conversations + + @property + def custom_channel_events(self): + if self._custom_channel_events is None: + from .custom_channel_events.client import AsyncCustomChannelEventsClient # noqa: E402 + + self._custom_channel_events = AsyncCustomChannelEventsClient(client_wrapper=self._client_wrapper) + return self._custom_channel_events + + @property + def custom_object_instances(self): + if self._custom_object_instances is None: + from .custom_object_instances.client import AsyncCustomObjectInstancesClient # noqa: E402 + + self._custom_object_instances = AsyncCustomObjectInstancesClient(client_wrapper=self._client_wrapper) + return self._custom_object_instances + + @property + def data_attributes(self): + if self._data_attributes is None: + from .data_attributes.client import AsyncDataAttributesClient # noqa: E402 + + self._data_attributes = AsyncDataAttributesClient(client_wrapper=self._client_wrapper) + return self._data_attributes + + @property + def events(self): + if self._events is None: + from .events.client import AsyncEventsClient # noqa: E402 + + self._events = AsyncEventsClient(client_wrapper=self._client_wrapper) + return self._events + + @property + def jobs(self): + if self._jobs is None: + from .jobs.client import AsyncJobsClient # noqa: E402 + + self._jobs = AsyncJobsClient(client_wrapper=self._client_wrapper) + return self._jobs + + @property + def messages(self): + if self._messages is None: + from .messages.client import AsyncMessagesClient # noqa: E402 + + self._messages = AsyncMessagesClient(client_wrapper=self._client_wrapper) + return self._messages + + @property + def segments(self): + if self._segments is None: + from .segments.client import AsyncSegmentsClient # noqa: E402 + + self._segments = AsyncSegmentsClient(client_wrapper=self._client_wrapper) + return self._segments + + @property + def subscription_types(self): + if self._subscription_types is None: + from .subscription_types.client import AsyncSubscriptionTypesClient # noqa: E402 + + self._subscription_types = AsyncSubscriptionTypesClient(client_wrapper=self._client_wrapper) + return self._subscription_types + + @property + def phone_call_redirects(self): + if self._phone_call_redirects is None: + from .phone_call_redirects.client import AsyncPhoneCallRedirectsClient # noqa: E402 + + self._phone_call_redirects = AsyncPhoneCallRedirectsClient(client_wrapper=self._client_wrapper) + return self._phone_call_redirects + + @property + def calls(self): + if self._calls is None: + from .calls.client import AsyncCallsClient # noqa: E402 + + self._calls = AsyncCallsClient(client_wrapper=self._client_wrapper) + return self._calls + + @property + def teams(self): + if self._teams is None: + from .teams.client import AsyncTeamsClient # noqa: E402 + + self._teams = AsyncTeamsClient(client_wrapper=self._client_wrapper) + return self._teams + + @property + def ticket_states(self): + if self._ticket_states is None: + from .ticket_states.client import AsyncTicketStatesClient # noqa: E402 + + self._ticket_states = AsyncTicketStatesClient(client_wrapper=self._client_wrapper) + return self._ticket_states + + @property + def ticket_types(self): + if self._ticket_types is None: + from .ticket_types.client import AsyncTicketTypesClient # noqa: E402 + + self._ticket_types = AsyncTicketTypesClient(client_wrapper=self._client_wrapper) + return self._ticket_types + + @property + def tickets(self): + if self._tickets is None: + from .tickets.client import AsyncTicketsClient # noqa: E402 + + self._tickets = AsyncTicketsClient(client_wrapper=self._client_wrapper) + return self._tickets + + @property + def visitors(self): + if self._visitors is None: + from .visitors.client import AsyncVisitorsClient # noqa: E402 + + self._visitors = AsyncVisitorsClient(client_wrapper=self._client_wrapper) + return self._visitors + + @property + def news(self): + if self._news is None: + from .news.client import AsyncNewsClient # noqa: E402 + + self._news = AsyncNewsClient(client_wrapper=self._client_wrapper) + return self._news + + @property + def unstable(self): + if self._unstable is None: + from .unstable.client import AsyncUnstableClient # noqa: E402 + + self._unstable = AsyncUnstableClient(client_wrapper=self._client_wrapper) + return self._unstable + + +def _get_base_url(*, base_url: typing.Optional[str] = None, environment: IntercomEnvironment) -> str: + if base_url is not None: + return base_url + elif environment is not None: + return environment.value + else: + raise Exception("Please pass in either base_url or environment to construct the client") diff --git a/src/intercom/companies/__init__.py b/src/intercom/companies/__init__.py new file mode 100644 index 00000000..6650a34f --- /dev/null +++ b/src/intercom/companies/__init__.py @@ -0,0 +1,58 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + CompaniesRetrieveResponse, + CompaniesRetrieveResponse_Company, + CompaniesRetrieveResponse_List, + Company, + CompanyPlan, + CompanySegments, + CompanyTags, + ) +_dynamic_imports: typing.Dict[str, str] = { + "CompaniesRetrieveResponse": ".types", + "CompaniesRetrieveResponse_Company": ".types", + "CompaniesRetrieveResponse_List": ".types", + "Company": ".types", + "CompanyPlan": ".types", + "CompanySegments": ".types", + "CompanyTags": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "CompaniesRetrieveResponse", + "CompaniesRetrieveResponse_Company", + "CompaniesRetrieveResponse_List", + "Company", + "CompanyPlan", + "CompanySegments", + "CompanyTags", +] diff --git a/src/intercom/companies/client.py b/src/intercom/companies/client.py new file mode 100644 index 00000000..2a533816 --- /dev/null +++ b/src/intercom/companies/client.py @@ -0,0 +1,1116 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.pagination import AsyncPager, SyncPager +from ..core.request_options import RequestOptions +from ..types.company_attached_contacts import CompanyAttachedContacts +from ..types.company_attached_segments import CompanyAttachedSegments +from ..types.company_list import CompanyList +from ..types.company_scroll import CompanyScroll +from ..types.create_or_update_company_request import CreateOrUpdateCompanyRequest +from ..types.deleted_company_object import DeletedCompanyObject +from ..types.update_company_request_body import UpdateCompanyRequestBody +from .raw_client import AsyncRawCompaniesClient, RawCompaniesClient +from .types.companies_retrieve_response import CompaniesRetrieveResponse +from .types.company import Company + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class CompaniesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawCompaniesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawCompaniesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawCompaniesClient + """ + return self._raw_client + + def retrieve( + self, + *, + name: typing.Optional[str] = None, + company_id: typing.Optional[str] = None, + tag_id: typing.Optional[str] = None, + segment_id: typing.Optional[str] = None, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> CompaniesRetrieveResponse: + """ + You can fetch a single company by passing in `company_id` or `name`. + + `https://api.intercom.io/companies?name={name}` + + `https://api.intercom.io/companies?company_id={company_id}` + + You can fetch all companies and filter by `segment_id` or `tag_id` as a query parameter. + + `https://api.intercom.io/companies?tag_id={tag_id}` + + `https://api.intercom.io/companies?segment_id={segment_id}` + + Parameters + ---------- + name : typing.Optional[str] + The `name` of the company to filter by. + + company_id : typing.Optional[str] + The `company_id` of the company to filter by. + + tag_id : typing.Optional[str] + The `tag_id` of the company to filter by. + + segment_id : typing.Optional[str] + The `segment_id` of the company to filter by. + + 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 + ------- + CompaniesRetrieveResponse + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.companies.retrieve( + name="my company", + company_id="12345", + tag_id="678910", + segment_id="98765", + page=1, + per_page=1, + ) + """ + _response = self._raw_client.retrieve( + name=name, + company_id=company_id, + tag_id=tag_id, + segment_id=segment_id, + page=page, + per_page=per_page, + request_options=request_options, + ) + return _response.data + + def create_or_update( + self, + *, + request: typing.Optional[CreateOrUpdateCompanyRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Company: + """ + You can create or update a company. + + Companies will be only visible in Intercom when there is at least one associated user. + + Companies are looked up via `company_id` in a `POST` request, if not found via `company_id`, the new company will be created, if found, that company will be updated. + + {% admonition type="warning" name="Using `company_id`" %} + You can set a unique `company_id` value when creating a company. However, it is not possible to update `company_id`. Be sure to set a unique value once upon creation of the company. + {% /admonition %} + + Parameters + ---------- + request : typing.Optional[CreateOrUpdateCompanyRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Company + Successful + + Examples + -------- + from intercom import CreateOrUpdateCompanyRequest, Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.companies.create_or_update( + request=CreateOrUpdateCompanyRequest( + name="my company", + company_id="company_remote_id", + remote_created_at=1374138000, + ), + ) + """ + _response = self._raw_client.create_or_update(request=request, request_options=request_options) + return _response.data + + def find(self, company_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Company: + """ + You can fetch a single company. + + Parameters + ---------- + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Company + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.companies.find( + company_id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + ) + """ + _response = self._raw_client.find(company_id, request_options=request_options) + return _response.data + + def update( + self, + company_id: str, + *, + request: typing.Optional[UpdateCompanyRequestBody] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Company: + """ + You can update a single company using the Intercom provisioned `id`. + + {% admonition type="warning" name="Using `company_id`" %} + When updating a company it is not possible to update `company_id`. This can only be set once upon creation of the company. + {% /admonition %} + + Parameters + ---------- + company_id : str + The unique identifier for the company which is given by Intercom + + request : typing.Optional[UpdateCompanyRequestBody] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Company + Successful + + Examples + -------- + from intercom import Intercom, UpdateCompanyRequestBody + + client = Intercom( + token="YOUR_TOKEN", + ) + client.companies.update( + company_id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + request=UpdateCompanyRequestBody( + name="my company", + website="http://www.mycompany.com/", + ), + ) + """ + _response = self._raw_client.update(company_id, request=request, request_options=request_options) + return _response.data + + def delete( + self, company_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeletedCompanyObject: + """ + You can delete a single company. + + Parameters + ---------- + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeletedCompanyObject + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.companies.delete( + company_id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + ) + """ + _response = self._raw_client.delete(company_id, request_options=request_options) + return _response.data + + def list_attached_contacts( + self, company_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> CompanyAttachedContacts: + """ + You can fetch a list of all contacts that belong to a company. + + Parameters + ---------- + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CompanyAttachedContacts + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.companies.list_attached_contacts( + company_id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + ) + """ + _response = self._raw_client.list_attached_contacts(company_id, request_options=request_options) + return _response.data + + def list_attached_segments( + self, company_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> CompanyAttachedSegments: + """ + You can fetch a list of all segments that belong to a company. + + Parameters + ---------- + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CompanyAttachedSegments + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.companies.list_attached_segments( + company_id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + ) + """ + _response = self._raw_client.list_attached_segments(company_id, request_options=request_options) + return _response.data + + def list( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + order: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Company, CompanyList]: + """ + You can list companies. The company list is sorted by the `last_request_at` field and by default is ordered descending, most recently requested first. + + Note that the API does not include companies who have no associated users in list responses. + + When using the Companies endpoint and the pages object to iterate through the returned companies, there is a limit of 10,000 Companies that can be returned. If you need to list or iterate on more than 10,000 Companies, please use the [Scroll API](https://developers.intercom.com/reference#iterating-over-all-companies). + {% admonition type="warning" name="Pagination" %} + You can use pagination to limit the number of results returned. The default is `20` 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 return per page. Defaults to 15 + + order : typing.Optional[str] + `asc` or `desc`. Return the companies in ascending or descending order. Defaults to desc + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[Company, CompanyList] + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + response = client.companies.list( + page=1, + per_page=1, + order="desc", + ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.list(page=page, per_page=per_page, order=order, request_options=request_options) + + def scroll( + self, *, scroll_param: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None + ) -> SyncPager[Company, typing.Optional[CompanyScroll]]: + """ + The `list all companies` functionality does not work well for huge datasets, and can result in errors and performance problems when paging deeply. The Scroll API provides an efficient mechanism for iterating over all companies in a dataset. + + - Each app can only have 1 scroll open at a time. You'll get an error message if you try to have more than one open per app. + - If the scroll isn't used for 1 minute, it expires and calls with that scroll param will fail + - If the end of the scroll is reached, "companies" will be empty and the scroll parameter will expire + + {% admonition type="info" name="Scroll Parameter" %} + You can get the first page of companies by simply sending a GET request to the scroll endpoint. + For subsequent requests you will need to use the scroll parameter from the response. + {% /admonition %} + {% admonition type="danger" name="Scroll network timeouts" %} + Since scroll is often used on large datasets network errors such as timeouts can be encountered. When this occurs you will see a HTTP 500 error with the following message: + "Request failed due to an internal network error. Please restart the scroll operation." + If this happens, you will need to restart your scroll query: It is not possible to continue from a specific point when using scroll. + {% /admonition %} + + Parameters + ---------- + scroll_param : typing.Optional[str] + + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[Company, typing.Optional[CompanyScroll]] + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + response = client.companies.scroll( + scroll_param="scroll_param", + ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.scroll(scroll_param=scroll_param, request_options=request_options) + + def attach_contact( + self, contact_id: str, *, company_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> Company: + """ + You can attach a company to a single contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Company + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.companies.attach_contact( + contact_id="contact_id", + company_id="6762f09a1bb69f9f2193bb34", + ) + """ + _response = self._raw_client.attach_contact(contact_id, company_id=company_id, request_options=request_options) + return _response.data + + def detach_contact( + self, contact_id: str, company_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Company: + """ + You can detach a company from a single contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Company + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.companies.detach_contact( + contact_id="58a430d35458202d41b1e65b", + company_id="58a430d35458202d41b1e65b", + ) + """ + _response = self._raw_client.detach_contact(contact_id, company_id, request_options=request_options) + return _response.data + + +class AsyncCompaniesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawCompaniesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawCompaniesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawCompaniesClient + """ + return self._raw_client + + async def retrieve( + self, + *, + name: typing.Optional[str] = None, + company_id: typing.Optional[str] = None, + tag_id: typing.Optional[str] = None, + segment_id: typing.Optional[str] = None, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> CompaniesRetrieveResponse: + """ + You can fetch a single company by passing in `company_id` or `name`. + + `https://api.intercom.io/companies?name={name}` + + `https://api.intercom.io/companies?company_id={company_id}` + + You can fetch all companies and filter by `segment_id` or `tag_id` as a query parameter. + + `https://api.intercom.io/companies?tag_id={tag_id}` + + `https://api.intercom.io/companies?segment_id={segment_id}` + + Parameters + ---------- + name : typing.Optional[str] + The `name` of the company to filter by. + + company_id : typing.Optional[str] + The `company_id` of the company to filter by. + + tag_id : typing.Optional[str] + The `tag_id` of the company to filter by. + + segment_id : typing.Optional[str] + The `segment_id` of the company to filter by. + + 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 + ------- + CompaniesRetrieveResponse + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.companies.retrieve( + name="my company", + company_id="12345", + tag_id="678910", + segment_id="98765", + page=1, + per_page=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.retrieve( + name=name, + company_id=company_id, + tag_id=tag_id, + segment_id=segment_id, + page=page, + per_page=per_page, + request_options=request_options, + ) + return _response.data + + async def create_or_update( + self, + *, + request: typing.Optional[CreateOrUpdateCompanyRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Company: + """ + You can create or update a company. + + Companies will be only visible in Intercom when there is at least one associated user. + + Companies are looked up via `company_id` in a `POST` request, if not found via `company_id`, the new company will be created, if found, that company will be updated. + + {% admonition type="warning" name="Using `company_id`" %} + You can set a unique `company_id` value when creating a company. However, it is not possible to update `company_id`. Be sure to set a unique value once upon creation of the company. + {% /admonition %} + + Parameters + ---------- + request : typing.Optional[CreateOrUpdateCompanyRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Company + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom, CreateOrUpdateCompanyRequest + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.companies.create_or_update( + request=CreateOrUpdateCompanyRequest( + name="my company", + company_id="company_remote_id", + remote_created_at=1374138000, + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_or_update(request=request, request_options=request_options) + return _response.data + + async def find(self, company_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Company: + """ + You can fetch a single company. + + Parameters + ---------- + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Company + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.companies.find( + company_id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.find(company_id, request_options=request_options) + return _response.data + + async def update( + self, + company_id: str, + *, + request: typing.Optional[UpdateCompanyRequestBody] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Company: + """ + You can update a single company using the Intercom provisioned `id`. + + {% admonition type="warning" name="Using `company_id`" %} + When updating a company it is not possible to update `company_id`. This can only be set once upon creation of the company. + {% /admonition %} + + Parameters + ---------- + company_id : str + The unique identifier for the company which is given by Intercom + + request : typing.Optional[UpdateCompanyRequestBody] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Company + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom, UpdateCompanyRequestBody + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.companies.update( + company_id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + request=UpdateCompanyRequestBody( + name="my company", + website="http://www.mycompany.com/", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update(company_id, request=request, request_options=request_options) + return _response.data + + async def delete( + self, company_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeletedCompanyObject: + """ + You can delete a single company. + + Parameters + ---------- + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeletedCompanyObject + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.companies.delete( + company_id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete(company_id, request_options=request_options) + return _response.data + + async def list_attached_contacts( + self, company_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> CompanyAttachedContacts: + """ + You can fetch a list of all contacts that belong to a company. + + Parameters + ---------- + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CompanyAttachedContacts + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.companies.list_attached_contacts( + company_id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_attached_contacts(company_id, request_options=request_options) + return _response.data + + async def list_attached_segments( + self, company_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> CompanyAttachedSegments: + """ + You can fetch a list of all segments that belong to a company. + + Parameters + ---------- + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CompanyAttachedSegments + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.companies.list_attached_segments( + company_id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_attached_segments(company_id, request_options=request_options) + return _response.data + + async def list( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + order: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[Company, CompanyList]: + """ + You can list companies. The company list is sorted by the `last_request_at` field and by default is ordered descending, most recently requested first. + + Note that the API does not include companies who have no associated users in list responses. + + When using the Companies endpoint and the pages object to iterate through the returned companies, there is a limit of 10,000 Companies that can be returned. If you need to list or iterate on more than 10,000 Companies, please use the [Scroll API](https://developers.intercom.com/reference#iterating-over-all-companies). + {% admonition type="warning" name="Pagination" %} + You can use pagination to limit the number of results returned. The default is `20` 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 return per page. Defaults to 15 + + order : typing.Optional[str] + `asc` or `desc`. Return the companies in ascending or descending order. Defaults to desc + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[Company, CompanyList] + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + response = await client.companies.list( + page=1, + per_page=1, + order="desc", + ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page + + + asyncio.run(main()) + """ + return await self._raw_client.list(page=page, per_page=per_page, order=order, request_options=request_options) + + async def scroll( + self, *, scroll_param: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncPager[Company, typing.Optional[CompanyScroll]]: + """ + The `list all companies` functionality does not work well for huge datasets, and can result in errors and performance problems when paging deeply. The Scroll API provides an efficient mechanism for iterating over all companies in a dataset. + + - Each app can only have 1 scroll open at a time. You'll get an error message if you try to have more than one open per app. + - If the scroll isn't used for 1 minute, it expires and calls with that scroll param will fail + - If the end of the scroll is reached, "companies" will be empty and the scroll parameter will expire + + {% admonition type="info" name="Scroll Parameter" %} + You can get the first page of companies by simply sending a GET request to the scroll endpoint. + For subsequent requests you will need to use the scroll parameter from the response. + {% /admonition %} + {% admonition type="danger" name="Scroll network timeouts" %} + Since scroll is often used on large datasets network errors such as timeouts can be encountered. When this occurs you will see a HTTP 500 error with the following message: + "Request failed due to an internal network error. Please restart the scroll operation." + If this happens, you will need to restart your scroll query: It is not possible to continue from a specific point when using scroll. + {% /admonition %} + + Parameters + ---------- + scroll_param : typing.Optional[str] + + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[Company, typing.Optional[CompanyScroll]] + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + response = await client.companies.scroll( + scroll_param="scroll_param", + ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page + + + asyncio.run(main()) + """ + return await self._raw_client.scroll(scroll_param=scroll_param, request_options=request_options) + + async def attach_contact( + self, contact_id: str, *, company_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> Company: + """ + You can attach a company to a single contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Company + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.companies.attach_contact( + contact_id="contact_id", + company_id="6762f09a1bb69f9f2193bb34", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.attach_contact( + contact_id, company_id=company_id, request_options=request_options + ) + return _response.data + + async def detach_contact( + self, contact_id: str, company_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Company: + """ + You can detach a company from a single contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Company + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.companies.detach_contact( + contact_id="58a430d35458202d41b1e65b", + company_id="58a430d35458202d41b1e65b", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.detach_contact(contact_id, company_id, request_options=request_options) + return _response.data diff --git a/src/intercom/companies/raw_client.py b/src/intercom/companies/raw_client.py new file mode 100644 index 00000000..99107d59 --- /dev/null +++ b/src/intercom/companies/raw_client.py @@ -0,0 +1,1669 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.bad_request_error import BadRequestError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.company_attached_contacts import CompanyAttachedContacts +from ..types.company_attached_segments import CompanyAttachedSegments +from ..types.company_list import CompanyList +from ..types.company_scroll import CompanyScroll +from ..types.create_or_update_company_request import CreateOrUpdateCompanyRequest +from ..types.deleted_company_object import DeletedCompanyObject +from ..types.error import Error +from ..types.update_company_request_body import UpdateCompanyRequestBody +from .types.companies_retrieve_response import CompaniesRetrieveResponse +from .types.company import Company + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawCompaniesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def retrieve( + self, + *, + name: typing.Optional[str] = None, + company_id: typing.Optional[str] = None, + tag_id: typing.Optional[str] = None, + segment_id: typing.Optional[str] = None, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[CompaniesRetrieveResponse]: + """ + You can fetch a single company by passing in `company_id` or `name`. + + `https://api.intercom.io/companies?name={name}` + + `https://api.intercom.io/companies?company_id={company_id}` + + You can fetch all companies and filter by `segment_id` or `tag_id` as a query parameter. + + `https://api.intercom.io/companies?tag_id={tag_id}` + + `https://api.intercom.io/companies?segment_id={segment_id}` + + Parameters + ---------- + name : typing.Optional[str] + The `name` of the company to filter by. + + company_id : typing.Optional[str] + The `company_id` of the company to filter by. + + tag_id : typing.Optional[str] + The `tag_id` of the company to filter by. + + segment_id : typing.Optional[str] + The `segment_id` of the company to filter by. + + 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 + ------- + HttpResponse[CompaniesRetrieveResponse] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + "companies", + method="GET", + params={ + "name": name, + "company_id": company_id, + "tag_id": tag_id, + "segment_id": segment_id, + "page": page, + "per_page": per_page, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CompaniesRetrieveResponse, + construct_type( + type_=CompaniesRetrieveResponse, # 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 create_or_update( + self, + *, + request: typing.Optional[CreateOrUpdateCompanyRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Company]: + """ + You can create or update a company. + + Companies will be only visible in Intercom when there is at least one associated user. + + Companies are looked up via `company_id` in a `POST` request, if not found via `company_id`, the new company will be created, if found, that company will be updated. + + {% admonition type="warning" name="Using `company_id`" %} + You can set a unique `company_id` value when creating a company. However, it is not possible to update `company_id`. Be sure to set a unique value once upon creation of the company. + {% /admonition %} + + Parameters + ---------- + request : typing.Optional[CreateOrUpdateCompanyRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Company] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + "companies", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateOrUpdateCompanyRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Company, + construct_type( + type_=Company, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 find( + self, company_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Company]: + """ + You can fetch a single company. + + Parameters + ---------- + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Company] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(company_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Company, + construct_type( + type_=Company, # 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 update( + self, + company_id: str, + *, + request: typing.Optional[UpdateCompanyRequestBody] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Company]: + """ + You can update a single company using the Intercom provisioned `id`. + + {% admonition type="warning" name="Using `company_id`" %} + When updating a company it is not possible to update `company_id`. This can only be set once upon creation of the company. + {% /admonition %} + + Parameters + ---------- + company_id : str + The unique identifier for the company which is given by Intercom + + request : typing.Optional[UpdateCompanyRequestBody] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Company] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(company_id)}", + method="PUT", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=UpdateCompanyRequestBody, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Company, + construct_type( + type_=Company, # 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 delete( + self, company_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DeletedCompanyObject]: + """ + You can delete a single company. + + Parameters + ---------- + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeletedCompanyObject] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(company_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeletedCompanyObject, + construct_type( + type_=DeletedCompanyObject, # 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_contacts( + self, company_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[CompanyAttachedContacts]: + """ + You can fetch a list of all contacts that belong to a company. + + Parameters + ---------- + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CompanyAttachedContacts] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(company_id)}/contacts", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CompanyAttachedContacts, + construct_type( + type_=CompanyAttachedContacts, # 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_segments( + self, company_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[CompanyAttachedSegments]: + """ + You can fetch a list of all segments that belong to a company. + + Parameters + ---------- + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CompanyAttachedSegments] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(company_id)}/segments", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CompanyAttachedSegments, + construct_type( + type_=CompanyAttachedSegments, # 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( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + order: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Company, CompanyList]: + """ + You can list companies. The company list is sorted by the `last_request_at` field and by default is ordered descending, most recently requested first. + + Note that the API does not include companies who have no associated users in list responses. + + When using the Companies endpoint and the pages object to iterate through the returned companies, there is a limit of 10,000 Companies that can be returned. If you need to list or iterate on more than 10,000 Companies, please use the [Scroll API](https://developers.intercom.com/reference#iterating-over-all-companies). + {% admonition type="warning" name="Pagination" %} + You can use pagination to limit the number of results returned. The default is `20` 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 return per page. Defaults to 15 + + order : typing.Optional[str] + `asc` or `desc`. Return the companies in ascending or descending order. Defaults to desc + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[Company, CompanyList] + Successful + """ + page = page if page is not None else 1 + + _response = self._client_wrapper.httpx_client.request( + "companies/list", + method="POST", + params={ + "page": page, + "per_page": per_page, + "order": order, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + CompanyList, + construct_type( + type_=CompanyList, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.data + _has_next = True + _get_next = lambda: self.list( + page=page + 1, + per_page=per_page, + order=order, + 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 scroll( + self, *, scroll_param: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None + ) -> SyncPager[Company, typing.Optional[CompanyScroll]]: + """ + The `list all companies` functionality does not work well for huge datasets, and can result in errors and performance problems when paging deeply. The Scroll API provides an efficient mechanism for iterating over all companies in a dataset. + + - Each app can only have 1 scroll open at a time. You'll get an error message if you try to have more than one open per app. + - If the scroll isn't used for 1 minute, it expires and calls with that scroll param will fail + - If the end of the scroll is reached, "companies" will be empty and the scroll parameter will expire + + {% admonition type="info" name="Scroll Parameter" %} + You can get the first page of companies by simply sending a GET request to the scroll endpoint. + For subsequent requests you will need to use the scroll parameter from the response. + {% /admonition %} + {% admonition type="danger" name="Scroll network timeouts" %} + Since scroll is often used on large datasets network errors such as timeouts can be encountered. When this occurs you will see a HTTP 500 error with the following message: + "Request failed due to an internal network error. Please restart the scroll operation." + If this happens, you will need to restart your scroll query: It is not possible to continue from a specific point when using scroll. + {% /admonition %} + + Parameters + ---------- + scroll_param : typing.Optional[str] + + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[Company, typing.Optional[CompanyScroll]] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + "companies/scroll", + method="GET", + params={ + "scroll_param": scroll_param, + }, + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return SyncPager(has_next=False, items=[], get_next=None, response=None) + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + typing.Optional[CompanyScroll], + construct_type( + type_=typing.Optional[CompanyScroll], # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.data if _parsed_response is not None else [] + + _has_next = False + _get_next = None + if _parsed_response is not None: + _parsed_next = _parsed_response.scroll_param + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.scroll( + scroll_param=_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 attach_contact( + self, contact_id: str, *, company_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Company]: + """ + You can attach a company to a single contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Company] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(contact_id)}/companies", + method="POST", + json={ + "id": company_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Company, + construct_type( + type_=Company, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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_contact( + self, contact_id: str, company_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Company]: + """ + You can detach a company from a single contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Company] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(contact_id)}/companies/{jsonable_encoder(company_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Company, + construct_type( + type_=Company, # 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) + + +class AsyncRawCompaniesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def retrieve( + self, + *, + name: typing.Optional[str] = None, + company_id: typing.Optional[str] = None, + tag_id: typing.Optional[str] = None, + segment_id: typing.Optional[str] = None, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[CompaniesRetrieveResponse]: + """ + You can fetch a single company by passing in `company_id` or `name`. + + `https://api.intercom.io/companies?name={name}` + + `https://api.intercom.io/companies?company_id={company_id}` + + You can fetch all companies and filter by `segment_id` or `tag_id` as a query parameter. + + `https://api.intercom.io/companies?tag_id={tag_id}` + + `https://api.intercom.io/companies?segment_id={segment_id}` + + Parameters + ---------- + name : typing.Optional[str] + The `name` of the company to filter by. + + company_id : typing.Optional[str] + The `company_id` of the company to filter by. + + tag_id : typing.Optional[str] + The `tag_id` of the company to filter by. + + segment_id : typing.Optional[str] + The `segment_id` of the company to filter by. + + 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 + ------- + AsyncHttpResponse[CompaniesRetrieveResponse] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + "companies", + method="GET", + params={ + "name": name, + "company_id": company_id, + "tag_id": tag_id, + "segment_id": segment_id, + "page": page, + "per_page": per_page, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CompaniesRetrieveResponse, + construct_type( + type_=CompaniesRetrieveResponse, # 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 create_or_update( + self, + *, + request: typing.Optional[CreateOrUpdateCompanyRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Company]: + """ + You can create or update a company. + + Companies will be only visible in Intercom when there is at least one associated user. + + Companies are looked up via `company_id` in a `POST` request, if not found via `company_id`, the new company will be created, if found, that company will be updated. + + {% admonition type="warning" name="Using `company_id`" %} + You can set a unique `company_id` value when creating a company. However, it is not possible to update `company_id`. Be sure to set a unique value once upon creation of the company. + {% /admonition %} + + Parameters + ---------- + request : typing.Optional[CreateOrUpdateCompanyRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Company] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + "companies", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateOrUpdateCompanyRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Company, + construct_type( + type_=Company, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 find( + self, company_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Company]: + """ + You can fetch a single company. + + Parameters + ---------- + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Company] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(company_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Company, + construct_type( + type_=Company, # 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 update( + self, + company_id: str, + *, + request: typing.Optional[UpdateCompanyRequestBody] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Company]: + """ + You can update a single company using the Intercom provisioned `id`. + + {% admonition type="warning" name="Using `company_id`" %} + When updating a company it is not possible to update `company_id`. This can only be set once upon creation of the company. + {% /admonition %} + + Parameters + ---------- + company_id : str + The unique identifier for the company which is given by Intercom + + request : typing.Optional[UpdateCompanyRequestBody] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Company] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(company_id)}", + method="PUT", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=UpdateCompanyRequestBody, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Company, + construct_type( + type_=Company, # 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 delete( + self, company_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DeletedCompanyObject]: + """ + You can delete a single company. + + Parameters + ---------- + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeletedCompanyObject] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(company_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeletedCompanyObject, + construct_type( + type_=DeletedCompanyObject, # 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_contacts( + self, company_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[CompanyAttachedContacts]: + """ + You can fetch a list of all contacts that belong to a company. + + Parameters + ---------- + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CompanyAttachedContacts] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(company_id)}/contacts", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CompanyAttachedContacts, + construct_type( + type_=CompanyAttachedContacts, # 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_segments( + self, company_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[CompanyAttachedSegments]: + """ + You can fetch a list of all segments that belong to a company. + + Parameters + ---------- + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CompanyAttachedSegments] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(company_id)}/segments", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CompanyAttachedSegments, + construct_type( + type_=CompanyAttachedSegments, # 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( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + order: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[Company, CompanyList]: + """ + You can list companies. The company list is sorted by the `last_request_at` field and by default is ordered descending, most recently requested first. + + Note that the API does not include companies who have no associated users in list responses. + + When using the Companies endpoint and the pages object to iterate through the returned companies, there is a limit of 10,000 Companies that can be returned. If you need to list or iterate on more than 10,000 Companies, please use the [Scroll API](https://developers.intercom.com/reference#iterating-over-all-companies). + {% admonition type="warning" name="Pagination" %} + You can use pagination to limit the number of results returned. The default is `20` 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 return per page. Defaults to 15 + + order : typing.Optional[str] + `asc` or `desc`. Return the companies in ascending or descending order. Defaults to desc + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[Company, CompanyList] + Successful + """ + page = page if page is not None else 1 + + _response = await self._client_wrapper.httpx_client.request( + "companies/list", + method="POST", + params={ + "page": page, + "per_page": per_page, + "order": order, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + CompanyList, + construct_type( + type_=CompanyList, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.data + _has_next = True + + async def _get_next(): + return await self.list( + page=page + 1, + per_page=per_page, + order=order, + 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 scroll( + self, *, scroll_param: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncPager[Company, typing.Optional[CompanyScroll]]: + """ + The `list all companies` functionality does not work well for huge datasets, and can result in errors and performance problems when paging deeply. The Scroll API provides an efficient mechanism for iterating over all companies in a dataset. + + - Each app can only have 1 scroll open at a time. You'll get an error message if you try to have more than one open per app. + - If the scroll isn't used for 1 minute, it expires and calls with that scroll param will fail + - If the end of the scroll is reached, "companies" will be empty and the scroll parameter will expire + + {% admonition type="info" name="Scroll Parameter" %} + You can get the first page of companies by simply sending a GET request to the scroll endpoint. + For subsequent requests you will need to use the scroll parameter from the response. + {% /admonition %} + {% admonition type="danger" name="Scroll network timeouts" %} + Since scroll is often used on large datasets network errors such as timeouts can be encountered. When this occurs you will see a HTTP 500 error with the following message: + "Request failed due to an internal network error. Please restart the scroll operation." + If this happens, you will need to restart your scroll query: It is not possible to continue from a specific point when using scroll. + {% /admonition %} + + Parameters + ---------- + scroll_param : typing.Optional[str] + + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[Company, typing.Optional[CompanyScroll]] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + "companies/scroll", + method="GET", + params={ + "scroll_param": scroll_param, + }, + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncPager(has_next=False, items=[], get_next=None, response=None) + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + typing.Optional[CompanyScroll], + construct_type( + type_=typing.Optional[CompanyScroll], # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.data if _parsed_response is not None else [] + + _has_next = False + _get_next = None + if _parsed_response is not None: + _parsed_next = _parsed_response.scroll_param + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.scroll( + scroll_param=_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 attach_contact( + self, contact_id: str, *, company_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Company]: + """ + You can attach a company to a single contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Company] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(contact_id)}/companies", + method="POST", + json={ + "id": company_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Company, + construct_type( + type_=Company, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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_contact( + self, contact_id: str, company_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Company]: + """ + You can detach a company from a single contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Company] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(contact_id)}/companies/{jsonable_encoder(company_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Company, + construct_type( + type_=Company, # 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) diff --git a/src/intercom/companies/types/__init__.py b/src/intercom/companies/types/__init__.py new file mode 100644 index 00000000..351a8adb --- /dev/null +++ b/src/intercom/companies/types/__init__.py @@ -0,0 +1,58 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .companies_retrieve_response import ( + CompaniesRetrieveResponse, + CompaniesRetrieveResponse_Company, + CompaniesRetrieveResponse_List, + ) + from .company import Company + from .company_plan import CompanyPlan + from .company_segments import CompanySegments + from .company_tags import CompanyTags +_dynamic_imports: typing.Dict[str, str] = { + "CompaniesRetrieveResponse": ".companies_retrieve_response", + "CompaniesRetrieveResponse_Company": ".companies_retrieve_response", + "CompaniesRetrieveResponse_List": ".companies_retrieve_response", + "Company": ".company", + "CompanyPlan": ".company_plan", + "CompanySegments": ".company_segments", + "CompanyTags": ".company_tags", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "CompaniesRetrieveResponse", + "CompaniesRetrieveResponse_Company", + "CompaniesRetrieveResponse_List", + "Company", + "CompanyPlan", + "CompanySegments", + "CompanyTags", +] diff --git a/src/intercom/companies/types/companies_retrieve_response.py b/src/intercom/companies/types/companies_retrieve_response.py new file mode 100644 index 00000000..6b435115 --- /dev/null +++ b/src/intercom/companies/types/companies_retrieve_response.py @@ -0,0 +1,67 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel, UnionMetadata +from ...types.offset_pages import OffsetPages +from .company import Company +from .company_plan import CompanyPlan +from .company_segments import CompanySegments +from .company_tags import CompanyTags + + +class CompaniesRetrieveResponse_Company(UncheckedBaseModel): + type: typing.Literal["company"] = "company" + id: str + name: str + app_id: str + plan: typing.Optional[CompanyPlan] = None + company_id: str + remote_created_at: typing.Optional[int] = None + created_at: int + updated_at: int + last_request_at: typing.Optional[int] = None + size: typing.Optional[int] = None + website: typing.Optional[str] = None + industry: typing.Optional[str] = None + monthly_spend: int + session_count: int + user_count: int + custom_attributes: typing.Optional[typing.Dict[str, typing.Any]] = None + tags: typing.Optional[CompanyTags] = None + segments: typing.Optional[CompanySegments] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class CompaniesRetrieveResponse_List(UncheckedBaseModel): + type: typing.Literal["list"] = "list" + pages: typing.Optional[OffsetPages] = None + total_count: typing.Optional[int] = None + data: typing.Optional[typing.List[Company]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +CompaniesRetrieveResponse = typing_extensions.Annotated[ + typing.Union[CompaniesRetrieveResponse_Company, CompaniesRetrieveResponse_List], UnionMetadata(discriminant="type") +] diff --git a/src/intercom/companies/types/company.py b/src/intercom/companies/types/company.py new file mode 100644 index 00000000..b3bffeb5 --- /dev/null +++ b/src/intercom/companies/types/company.py @@ -0,0 +1,116 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .company_plan import CompanyPlan +from .company_segments import CompanySegments +from .company_tags import CompanyTags + + +class Company(UncheckedBaseModel): + """ + Companies allow you to represent organizations using your product. Each company will have its own description and be associated with contacts. You can fetch, create, update and list companies. + """ + + type: typing.Optional[typing.Literal["company"]] = pydantic.Field(default=None) + """ + Value is `company` + """ + + id: str = pydantic.Field() + """ + The Intercom defined id representing the company. + """ + + name: str = pydantic.Field() + """ + The name of the company. + """ + + app_id: str = pydantic.Field() + """ + The Intercom defined code of the workspace the company is associated to. + """ + + plan: typing.Optional[CompanyPlan] = None + company_id: str = pydantic.Field() + """ + The company id you have defined for the company. + """ + + remote_created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the company was created by you. + """ + + created_at: int = pydantic.Field() + """ + The time the company was added in Intercom. + """ + + updated_at: int = pydantic.Field() + """ + The last time the company was updated. + """ + + last_request_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the company last recorded making a request. + """ + + size: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of employees in the company. + """ + + website: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL for the company website. + """ + + industry: typing.Optional[str] = pydantic.Field(default=None) + """ + The industry that the company operates in. + """ + + monthly_spend: int = pydantic.Field() + """ + How much revenue the company generates for your business. + """ + + session_count: int = pydantic.Field() + """ + How many sessions the company has recorded. + """ + + user_count: int = pydantic.Field() + """ + The number of users in the company. + """ + + custom_attributes: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + The custom attributes you have set on the company. + """ + + tags: typing.Optional[CompanyTags] = pydantic.Field(default=None) + """ + The list of tags associated with the company + """ + + segments: typing.Optional[CompanySegments] = pydantic.Field(default=None) + """ + The list of segments associated with the company + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/companies/types/company_plan.py b/src/intercom/companies/types/company_plan.py new file mode 100644 index 00000000..613cb835 --- /dev/null +++ b/src/intercom/companies/types/company_plan.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CompanyPlan(UncheckedBaseModel): + type: typing.Optional[str] = pydantic.Field(default=None) + """ + Value is always "plan" + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the plan + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the plan + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/companies/types/company_segments.py b/src/intercom/companies/types/company_segments.py new file mode 100644 index 00000000..ee2e0f2a --- /dev/null +++ b/src/intercom/companies/types/company_segments.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...segments.types.segment import Segment + + +class CompanySegments(UncheckedBaseModel): + """ + The list of segments associated with the company + """ + + type: typing.Optional[typing.Literal["segment.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + segments: typing.Optional[typing.List[Segment]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/companies/types/company_tags.py b/src/intercom/companies/types/company_tags.py new file mode 100644 index 00000000..003dc1b4 --- /dev/null +++ b/src/intercom/companies/types/company_tags.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CompanyTags(UncheckedBaseModel): + """ + The list of tags associated with the company + """ + + type: typing.Optional[typing.Literal["tag.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + tags: typing.Optional[typing.List[typing.Any]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/contacts/__init__.py b/src/intercom/contacts/__init__.py new file mode 100644 index 00000000..512b43bd --- /dev/null +++ b/src/intercom/contacts/__init__.py @@ -0,0 +1,55 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + Contact, + ContactsCreateResponse, + ContactsFindResponse, + ContactsMergeLeadInUserResponse, + ContactsUpdateResponse, + ShowContactByExternalIdResponse, + ) +_dynamic_imports: typing.Dict[str, str] = { + "Contact": ".types", + "ContactsCreateResponse": ".types", + "ContactsFindResponse": ".types", + "ContactsMergeLeadInUserResponse": ".types", + "ContactsUpdateResponse": ".types", + "ShowContactByExternalIdResponse": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "Contact", + "ContactsCreateResponse", + "ContactsFindResponse", + "ContactsMergeLeadInUserResponse", + "ContactsUpdateResponse", + "ShowContactByExternalIdResponse", +] diff --git a/src/intercom/contacts/client.py b/src/intercom/contacts/client.py new file mode 100644 index 00000000..42aa9c37 --- /dev/null +++ b/src/intercom/contacts/client.py @@ -0,0 +1,1866 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..companies.types.company import Company +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.pagination import AsyncPager, SyncPager +from ..core.request_options import RequestOptions +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.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 .raw_client import AsyncRawContactsClient, RawContactsClient +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 ContactsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawContactsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawContactsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawContactsClient + """ + return self._raw_client + + 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 + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + response = client.contacts.list_attached_companies( + contact_id="63a07ddf05a32042dffac965", + page=1, + per_page=1, + ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.list_attached_companies( + contact_id, page=page, per_page=per_page, request_options=request_options + ) + + def list_attached_segments( + self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + ------- + ContactSegments + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.contacts.list_attached_segments( + contact_id="63a07ddf05a32042dffac965", + ) + """ + _response = self._raw_client.list_attached_segments(contact_id, request_options=request_options) + return _response.data + + def list_attached_subscriptions( + self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + ------- + SubscriptionTypeList + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.contacts.list_attached_subscriptions( + contact_id="63a07ddf05a32042dffac965", + ) + """ + _response = self._raw_client.list_attached_subscriptions(contact_id, request_options=request_options) + return _response.data + + def attach_subscription( + self, + contact_id: str, + *, + subscription_id: str, + consent_type: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> 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 + ------- + SubscriptionType + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.contacts.attach_subscription( + contact_id="63a07ddf05a32042dffac965", + subscription_id="37846", + consent_type="opt_in", + ) + """ + _response = self._raw_client.attach_subscription( + contact_id, subscription_id=subscription_id, consent_type=consent_type, request_options=request_options + ) + return _response.data + + def detach_subscription( + self, contact_id: str, subscription_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + ------- + SubscriptionType + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.contacts.detach_subscription( + contact_id="63a07ddf05a32042dffac965", + subscription_id="37846", + ) + """ + _response = self._raw_client.detach_subscription(contact_id, subscription_id, request_options=request_options) + return _response.data + + def list_attached_tags( + self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + ------- + TagList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.contacts.list_attached_tags( + contact_id="63a07ddf05a32042dffac965", + ) + """ + _response = self._raw_client.list_attached_tags(contact_id, request_options=request_options) + return _response.data + + def find(self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> 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 + ------- + ContactsFindResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.contacts.find( + contact_id="63a07ddf05a32042dffac965", + ) + """ + _response = self._raw_client.find(contact_id, request_options=request_options) + return _response.data + + 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, + ) -> 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 + ------- + ContactsUpdateResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.contacts.update( + contact_id="63a07ddf05a32042dffac965", + email="joebloggs@intercom.io", + name="joe bloggs", + ) + """ + _response = self._raw_client.update( + contact_id, + 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, + request_options=request_options, + ) + return _response.data + + def delete(self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> ContactDeleted: + """ + You can delete a single contact. + + Parameters + ---------- + contact_id : str + contact_id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContactDeleted + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.contacts.delete( + contact_id="contact_id", + ) + """ + _response = self._raw_client.delete(contact_id, request_options=request_options) + return _response.data + + def merge_lead_in_user( + self, + *, + lead_id: typing.Optional[str] = OMIT, + contact_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> 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 + ------- + ContactsMergeLeadInUserResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.contacts.merge_lead_in_user( + lead_id="6762f0d51bb69f9f2193bb7f", + contact_id="6762f0d51bb69f9f2193bb80", + ) + """ + _response = self._raw_client.merge_lead_in_user( + lead_id=lead_id, contact_id=contact_id, request_options=request_options + ) + return _response.data + + 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
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
Shortcut for `OR` queries
Values must be in Array | + | NIN | All | Not In
Shortcut for `OR !` queries
Values must be in Array | + | > | Integer
Date (UNIX Timestamp) | Greater than | + | < | Integer
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 + + Examples + -------- + from intercom import ( + Intercom, + MultipleFilterSearchRequest, + SingleFilterSearchRequest, + StartingAfterPaging, + ) + + client = Intercom( + token="YOUR_TOKEN", + ) + response = client.contacts.search( + query=MultipleFilterSearchRequest( + operator="AND", + value=[ + SingleFilterSearchRequest( + field="created_at", + operator=">", + value="1306054154", + ) + ], + ), + pagination=StartingAfterPaging( + per_page=5, + ), + ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.search(query=query, pagination=pagination, request_options=request_options) + + 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 + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + response = client.contacts.list() + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.list( + page=page, per_page=per_page, starting_after=starting_after, request_options=request_options + ) + + def create( + self, *, request: CreateContactRequest, request_options: typing.Optional[RequestOptions] = None + ) -> ContactsCreateResponse: + """ + You can create a new contact (ie. user or lead). + + Parameters + ---------- + request : CreateContactRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContactsCreateResponse + successful + + Examples + -------- + from intercom import CreateContactRequestWithEmail, Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.contacts.create( + request=CreateContactRequestWithEmail( + email="joebloggs@intercom.io", + ), + ) + """ + _response = self._raw_client.create(request=request, request_options=request_options) + return _response.data + + def show_contact_by_external_id( + self, external_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + ------- + ShowContactByExternalIdResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.contacts.show_contact_by_external_id( + external_id="cdd29344-5e0c-4ef0-ac56-f9ba2979bc27", + ) + """ + _response = self._raw_client.show_contact_by_external_id(external_id, request_options=request_options) + return _response.data + + def archive(self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> ContactArchived: + """ + You can archive a single contact. + + Parameters + ---------- + contact_id : str + contact_id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContactArchived + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.contacts.archive( + contact_id="63a07ddf05a32042dffac965", + ) + """ + _response = self._raw_client.archive(contact_id, request_options=request_options) + return _response.data + + def unarchive( + self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContactUnarchived: + """ + You can unarchive a single contact. + + Parameters + ---------- + contact_id : str + contact_id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContactUnarchived + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.contacts.unarchive( + contact_id="63a07ddf05a32042dffac965", + ) + """ + _response = self._raw_client.unarchive(contact_id, request_options=request_options) + return _response.data + + def block_contact( + self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContactBlocked: + """ + Block a single contact.
**Note:** conversations of the contact will also be archived during the process.
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 + ------- + ContactBlocked + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.contacts.block_contact( + contact_id="63a07ddf05a32042dffac965", + ) + """ + _response = self._raw_client.block_contact(contact_id, request_options=request_options) + return _response.data + + +class AsyncContactsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawContactsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawContactsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawContactsClient + """ + return self._raw_client + + 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 + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + response = await client.contacts.list_attached_companies( + contact_id="63a07ddf05a32042dffac965", + page=1, + per_page=1, + ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page + + + asyncio.run(main()) + """ + return await self._raw_client.list_attached_companies( + contact_id, page=page, per_page=per_page, request_options=request_options + ) + + async def list_attached_segments( + self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + ------- + ContactSegments + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.contacts.list_attached_segments( + contact_id="63a07ddf05a32042dffac965", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_attached_segments(contact_id, request_options=request_options) + return _response.data + + async def list_attached_subscriptions( + self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + ------- + SubscriptionTypeList + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.contacts.list_attached_subscriptions( + contact_id="63a07ddf05a32042dffac965", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_attached_subscriptions(contact_id, request_options=request_options) + return _response.data + + async def attach_subscription( + self, + contact_id: str, + *, + subscription_id: str, + consent_type: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> 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 + ------- + SubscriptionType + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.contacts.attach_subscription( + contact_id="63a07ddf05a32042dffac965", + subscription_id="37846", + consent_type="opt_in", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.attach_subscription( + contact_id, subscription_id=subscription_id, consent_type=consent_type, request_options=request_options + ) + return _response.data + + async def detach_subscription( + self, contact_id: str, subscription_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + ------- + SubscriptionType + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.contacts.detach_subscription( + contact_id="63a07ddf05a32042dffac965", + subscription_id="37846", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.detach_subscription( + contact_id, subscription_id, request_options=request_options + ) + return _response.data + + async def list_attached_tags( + self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + ------- + TagList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.contacts.list_attached_tags( + contact_id="63a07ddf05a32042dffac965", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_attached_tags(contact_id, request_options=request_options) + return _response.data + + async def find( + self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + ------- + ContactsFindResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.contacts.find( + contact_id="63a07ddf05a32042dffac965", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.find(contact_id, request_options=request_options) + return _response.data + + 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, + ) -> 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 + ------- + ContactsUpdateResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.contacts.update( + contact_id="63a07ddf05a32042dffac965", + email="joebloggs@intercom.io", + name="joe bloggs", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update( + contact_id, + 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, + request_options=request_options, + ) + return _response.data + + async def delete( + self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContactDeleted: + """ + You can delete a single contact. + + Parameters + ---------- + contact_id : str + contact_id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContactDeleted + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.contacts.delete( + contact_id="contact_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete(contact_id, request_options=request_options) + return _response.data + + 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, + ) -> 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 + ------- + ContactsMergeLeadInUserResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.contacts.merge_lead_in_user( + lead_id="6762f0d51bb69f9f2193bb7f", + contact_id="6762f0d51bb69f9f2193bb80", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.merge_lead_in_user( + lead_id=lead_id, contact_id=contact_id, request_options=request_options + ) + return _response.data + + 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
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
Shortcut for `OR` queries
Values must be in Array | + | NIN | All | Not In
Shortcut for `OR !` queries
Values must be in Array | + | > | Integer
Date (UNIX Timestamp) | Greater than | + | < | Integer
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 + + Examples + -------- + import asyncio + + from intercom import ( + AsyncIntercom, + MultipleFilterSearchRequest, + SingleFilterSearchRequest, + StartingAfterPaging, + ) + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + response = await client.contacts.search( + query=MultipleFilterSearchRequest( + operator="AND", + value=[ + SingleFilterSearchRequest( + field="created_at", + operator=">", + value="1306054154", + ) + ], + ), + pagination=StartingAfterPaging( + per_page=5, + ), + ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page + + + asyncio.run(main()) + """ + return await self._raw_client.search(query=query, pagination=pagination, request_options=request_options) + + 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 + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + response = await client.contacts.list() + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page + + + asyncio.run(main()) + """ + return await self._raw_client.list( + page=page, per_page=per_page, starting_after=starting_after, request_options=request_options + ) + + async def create( + self, *, request: CreateContactRequest, request_options: typing.Optional[RequestOptions] = None + ) -> ContactsCreateResponse: + """ + You can create a new contact (ie. user or lead). + + Parameters + ---------- + request : CreateContactRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContactsCreateResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom, CreateContactRequestWithEmail + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.contacts.create( + request=CreateContactRequestWithEmail( + email="joebloggs@intercom.io", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create(request=request, request_options=request_options) + return _response.data + + async def show_contact_by_external_id( + self, external_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + ------- + ShowContactByExternalIdResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.contacts.show_contact_by_external_id( + external_id="cdd29344-5e0c-4ef0-ac56-f9ba2979bc27", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.show_contact_by_external_id(external_id, request_options=request_options) + return _response.data + + async def archive( + self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContactArchived: + """ + You can archive a single contact. + + Parameters + ---------- + contact_id : str + contact_id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContactArchived + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.contacts.archive( + contact_id="63a07ddf05a32042dffac965", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.archive(contact_id, request_options=request_options) + return _response.data + + async def unarchive( + self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContactUnarchived: + """ + You can unarchive a single contact. + + Parameters + ---------- + contact_id : str + contact_id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContactUnarchived + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.contacts.unarchive( + contact_id="63a07ddf05a32042dffac965", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.unarchive(contact_id, request_options=request_options) + return _response.data + + async def block_contact( + self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContactBlocked: + """ + Block a single contact.
**Note:** conversations of the contact will also be archived during the process.
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 + ------- + ContactBlocked + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.contacts.block_contact( + contact_id="63a07ddf05a32042dffac965", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.block_contact(contact_id, request_options=request_options) + return _response.data diff --git a/src/intercom/contacts/raw_client.py b/src/intercom/contacts/raw_client.py new file mode 100644 index 00000000..fad47df9 --- /dev/null +++ b/src/intercom/contacts/raw_client.py @@ -0,0 +1,2440 @@ +# 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
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
Shortcut for `OR` queries
Values must be in Array | + | NIN | All | Not In
Shortcut for `OR !` queries
Values must be in Array | + | > | Integer
Date (UNIX Timestamp) | Greater than | + | < | Integer
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.
**Note:** conversations of the contact will also be archived during the process.
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
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
Shortcut for `OR` queries
Values must be in Array | + | NIN | All | Not In
Shortcut for `OR !` queries
Values must be in Array | + | > | Integer
Date (UNIX Timestamp) | Greater than | + | < | Integer
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.
**Note:** conversations of the contact will also be archived during the process.
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) diff --git a/src/intercom/contacts/types/__init__.py b/src/intercom/contacts/types/__init__.py new file mode 100644 index 00000000..6eb70cfb --- /dev/null +++ b/src/intercom/contacts/types/__init__.py @@ -0,0 +1,53 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .contact import Contact + from .contacts_create_response import ContactsCreateResponse + from .contacts_find_response import ContactsFindResponse + from .contacts_merge_lead_in_user_response import ContactsMergeLeadInUserResponse + from .contacts_update_response import ContactsUpdateResponse + from .show_contact_by_external_id_response import ShowContactByExternalIdResponse +_dynamic_imports: typing.Dict[str, str] = { + "Contact": ".contact", + "ContactsCreateResponse": ".contacts_create_response", + "ContactsFindResponse": ".contacts_find_response", + "ContactsMergeLeadInUserResponse": ".contacts_merge_lead_in_user_response", + "ContactsUpdateResponse": ".contacts_update_response", + "ShowContactByExternalIdResponse": ".show_contact_by_external_id_response", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "Contact", + "ContactsCreateResponse", + "ContactsFindResponse", + "ContactsMergeLeadInUserResponse", + "ContactsUpdateResponse", + "ShowContactByExternalIdResponse", +] diff --git a/src/intercom/contacts/types/contact.py b/src/intercom/contacts/types/contact.py new file mode 100644 index 00000000..bf522612 --- /dev/null +++ b/src/intercom/contacts/types/contact.py @@ -0,0 +1,233 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.contact_companies import ContactCompanies +from ...types.contact_location import ContactLocation +from ...types.contact_notes import ContactNotes +from ...types.contact_social_profiles import ContactSocialProfiles +from ...types.contact_tags import ContactTags + + +class Contact(UncheckedBaseModel): + """ + Contacts represent your leads and users in Intercom. + """ + + type: typing.Optional[typing.Literal["contact"]] = pydantic.Field(default=None) + """ + The type of object. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the contact which is given by Intercom. + """ + + external_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the contact which is provided by the Client. + """ + + workspace_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the workspace which the contact belongs to. + """ + + role: typing.Optional[str] = pydantic.Field(default=None) + """ + The role of the contact. + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + The contact's email. + """ + + email_domain: typing.Optional[str] = pydantic.Field(default=None) + """ + The contact's email domain. + """ + + phone: typing.Optional[str] = pydantic.Field(default=None) + """ + The contacts phone. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The contacts name. + """ + + owner_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of an admin that has been assigned account ownership of the contact. + """ + + has_hard_bounced: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the contact has had an email sent to them hard bounce. + """ + + marked_email_as_spam: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the contact has marked an email sent to them as spam. + """ + + unsubscribed_from_emails: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the contact is unsubscribed from emails. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + (UNIX timestamp) The time when the contact was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + (UNIX timestamp) The time when the contact was last updated. + """ + + signed_up_at: typing.Optional[int] = pydantic.Field(default=None) + """ + (UNIX timestamp) The time specified for when a contact signed up. + """ + + last_seen_at: typing.Optional[int] = pydantic.Field(default=None) + """ + (UNIX timestamp) The time when the contact was last seen (either where the Intercom Messenger was installed or when specified manually). + """ + + last_replied_at: typing.Optional[int] = pydantic.Field(default=None) + """ + (UNIX timestamp) The time when the contact last messaged in. + """ + + last_contacted_at: typing.Optional[int] = pydantic.Field(default=None) + """ + (UNIX timestamp) The time when the contact was last messaged. + """ + + last_email_opened_at: typing.Optional[int] = pydantic.Field(default=None) + """ + (UNIX timestamp) The time when the contact last opened an email. + """ + + last_email_clicked_at: typing.Optional[int] = pydantic.Field(default=None) + """ + (UNIX timestamp) The time when the contact last clicked a link in an email. + """ + + language_override: typing.Optional[str] = pydantic.Field(default=None) + """ + A preferred language setting for the contact, used by the Intercom Messenger even if their browser settings change. + """ + + browser: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the browser which the contact is using. + """ + + browser_version: typing.Optional[str] = pydantic.Field(default=None) + """ + The version of the browser which the contact is using. + """ + + browser_language: typing.Optional[str] = pydantic.Field(default=None) + """ + The language set by the browser which the contact is using. + """ + + os: typing.Optional[str] = pydantic.Field(default=None) + """ + The operating system which the contact is using. + """ + + android_app_name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the Android app which the contact is using. + """ + + android_app_version: typing.Optional[str] = pydantic.Field(default=None) + """ + The version of the Android app which the contact is using. + """ + + android_device: typing.Optional[str] = pydantic.Field(default=None) + """ + The Android device which the contact is using. + """ + + android_os_version: typing.Optional[str] = pydantic.Field(default=None) + """ + The version of the Android OS which the contact is using. + """ + + android_sdk_version: typing.Optional[str] = pydantic.Field(default=None) + """ + The version of the Android SDK which the contact is using. + """ + + android_last_seen_at: typing.Optional[int] = pydantic.Field(default=None) + """ + (UNIX timestamp) The time when the contact was last seen on an Android device. + """ + + ios_app_name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the iOS app which the contact is using. + """ + + ios_app_version: typing.Optional[str] = pydantic.Field(default=None) + """ + The version of the iOS app which the contact is using. + """ + + ios_device: typing.Optional[str] = pydantic.Field(default=None) + """ + The iOS device which the contact is using. + """ + + ios_os_version: typing.Optional[str] = pydantic.Field(default=None) + """ + The version of iOS which the contact is using. + """ + + ios_sdk_version: typing.Optional[str] = pydantic.Field(default=None) + """ + The version of the iOS SDK which the contact is using. + """ + + ios_last_seen_at: typing.Optional[int] = pydantic.Field(default=None) + """ + (UNIX timestamp) The last time the contact used the iOS app. + """ + + custom_attributes: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + The custom attributes which are set for the contact. + """ + + avatar: typing.Optional[str] = pydantic.Field(default=None) + """ + An image URL containing the avatar of a contact. + """ + + tags: typing.Optional[ContactTags] = None + notes: typing.Optional[ContactNotes] = None + companies: typing.Optional[ContactCompanies] = None + location: typing.Optional[ContactLocation] = None + social_profiles: typing.Optional[ContactSocialProfiles] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/contacts/types/contacts_create_response.py b/src/intercom/contacts/types/contacts_create_response.py new file mode 100644 index 00000000..7496002e --- /dev/null +++ b/src/intercom/contacts/types/contacts_create_response.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact import Contact + + +class ContactsCreateResponse(Contact): + enabled_push_messaging: typing.Optional[bool] = pydantic.Field(default=None) + """ + If the user has enabled push messaging. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/contacts/types/contacts_find_response.py b/src/intercom/contacts/types/contacts_find_response.py new file mode 100644 index 00000000..4b797d4f --- /dev/null +++ b/src/intercom/contacts/types/contacts_find_response.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact import Contact + + +class ContactsFindResponse(Contact): + enabled_push_messaging: typing.Optional[bool] = pydantic.Field(default=None) + """ + If the user has enabled push messaging. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/contacts/types/contacts_merge_lead_in_user_response.py b/src/intercom/contacts/types/contacts_merge_lead_in_user_response.py new file mode 100644 index 00000000..18d16973 --- /dev/null +++ b/src/intercom/contacts/types/contacts_merge_lead_in_user_response.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact import Contact + + +class ContactsMergeLeadInUserResponse(Contact): + enabled_push_messaging: typing.Optional[bool] = pydantic.Field(default=None) + """ + If the user has enabled push messaging. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/contacts/types/contacts_update_response.py b/src/intercom/contacts/types/contacts_update_response.py new file mode 100644 index 00000000..6dcec366 --- /dev/null +++ b/src/intercom/contacts/types/contacts_update_response.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact import Contact + + +class ContactsUpdateResponse(Contact): + enabled_push_messaging: typing.Optional[bool] = pydantic.Field(default=None) + """ + If the user has enabled push messaging. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/contacts/types/show_contact_by_external_id_response.py b/src/intercom/contacts/types/show_contact_by_external_id_response.py new file mode 100644 index 00000000..016134ab --- /dev/null +++ b/src/intercom/contacts/types/show_contact_by_external_id_response.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact import Contact + + +class ShowContactByExternalIdResponse(Contact): + enabled_push_messaging: typing.Optional[bool] = pydantic.Field(default=None) + """ + If the user has enabled push messaging. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/conversations/__init__.py b/src/intercom/conversations/__init__.py new file mode 100644 index 00000000..e3ddd0ad --- /dev/null +++ b/src/intercom/conversations/__init__.py @@ -0,0 +1,79 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + AttachContactToConversationRequestCustomer, + AttachContactToConversationRequestCustomerCustomer, + AttachContactToConversationRequestCustomerIntercomUserId, + AttachContactToConversationRequestCustomerUserId, + Conversation, + ConversationPriority, + ConversationState, + ConversationsManageRequestBody, + ConversationsManageRequestBody_Assignment, + ConversationsManageRequestBody_Close, + ConversationsManageRequestBody_Open, + ConversationsManageRequestBody_Snoozed, + CreateConversationRequestFrom, + CreateConversationRequestFromType, + ) +_dynamic_imports: typing.Dict[str, str] = { + "AttachContactToConversationRequestCustomer": ".types", + "AttachContactToConversationRequestCustomerCustomer": ".types", + "AttachContactToConversationRequestCustomerIntercomUserId": ".types", + "AttachContactToConversationRequestCustomerUserId": ".types", + "Conversation": ".types", + "ConversationPriority": ".types", + "ConversationState": ".types", + "ConversationsManageRequestBody": ".types", + "ConversationsManageRequestBody_Assignment": ".types", + "ConversationsManageRequestBody_Close": ".types", + "ConversationsManageRequestBody_Open": ".types", + "ConversationsManageRequestBody_Snoozed": ".types", + "CreateConversationRequestFrom": ".types", + "CreateConversationRequestFromType": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "AttachContactToConversationRequestCustomer", + "AttachContactToConversationRequestCustomerCustomer", + "AttachContactToConversationRequestCustomerIntercomUserId", + "AttachContactToConversationRequestCustomerUserId", + "Conversation", + "ConversationPriority", + "ConversationState", + "ConversationsManageRequestBody", + "ConversationsManageRequestBody_Assignment", + "ConversationsManageRequestBody_Close", + "ConversationsManageRequestBody_Open", + "ConversationsManageRequestBody_Snoozed", + "CreateConversationRequestFrom", + "CreateConversationRequestFromType", +] diff --git a/src/intercom/conversations/client.py b/src/intercom/conversations/client.py new file mode 100644 index 00000000..f3483200 --- /dev/null +++ b/src/intercom/conversations/client.py @@ -0,0 +1,1683 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.pagination import AsyncPager, SyncPager +from ..core.request_options import RequestOptions +from ..messages.types.message import Message +from ..tickets.types.ticket import Ticket +from ..types.conversation_deleted import ConversationDeleted +from ..types.conversation_list import ConversationList +from ..types.custom_attributes import CustomAttributes +from ..types.redact_conversation_request import RedactConversationRequest +from ..types.reply_conversation_request import ReplyConversationRequest +from ..types.search_request_query import SearchRequestQuery +from ..types.starting_after_paging import StartingAfterPaging +from ..types.ticket_request_custom_attributes import TicketRequestCustomAttributes +from .raw_client import AsyncRawConversationsClient, RawConversationsClient +from .types.attach_contact_to_conversation_request_customer import AttachContactToConversationRequestCustomer +from .types.conversation import Conversation +from .types.conversations_manage_request_body import ConversationsManageRequestBody +from .types.create_conversation_request_from import CreateConversationRequestFrom + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class ConversationsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawConversationsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawConversationsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawConversationsClient + """ + return self._raw_client + + def list( + self, + *, + per_page: typing.Optional[int] = None, + starting_after: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Conversation, ConversationList]: + """ + You can fetch a list of all conversations. + + You can optionally request the result page size and the cursor to start after to fetch the result. + {% admonition type="warning" name="Pagination" %} + You can use pagination to limit the number of results returned. The default is `20` 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 + ---------- + per_page : typing.Optional[int] + How many results per page + + starting_after : typing.Optional[str] + String used to get the next page of conversations. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[Conversation, ConversationList] + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + response = client.conversations.list( + per_page=1, + starting_after="starting_after", + ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.list(per_page=per_page, starting_after=starting_after, request_options=request_options) + + def create( + self, + *, + from_: CreateConversationRequestFrom, + body: str, + created_at: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Message: + """ + You can create a conversation that has been initiated by a contact (ie. user or lead). + The conversation can be an in-app message only. + + {% admonition type="info" name="Sending for visitors" %} + You can also send a message from a visitor by specifying their `user_id` or `id` value in the `from` field, along with a `type` field value of `contact`. + This visitor will be automatically converted to a contact with a lead role once the conversation is created. + {% /admonition %} + + This will return the Message model that has been created. + + Parameters + ---------- + from_ : CreateConversationRequestFrom + + body : str + The content of the message. HTML is not supported. + + created_at : typing.Optional[int] + The time the conversation was created as a UTC Unix timestamp. If not provided, the current time will be used. This field is only recommneded for migrating past conversations from another source into Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Message + conversation created + + Examples + -------- + from intercom import Intercom + from intercom.conversations import CreateConversationRequestFrom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.conversations.create( + from_=CreateConversationRequestFrom( + type="user", + id="6762f11b1bb69f9f2193bba3", + ), + body="Hello there", + ) + """ + _response = self._raw_client.create( + from_=from_, body=body, created_at=created_at, request_options=request_options + ) + return _response.data + + def find( + self, + conversation_id: str, + *, + display_as: typing.Optional[str] = None, + include_translations: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + + You can fetch the details of a single conversation. + + This will return a single Conversation model with all its conversation parts. + + {% admonition type="warning" name="Hard limit of 500 parts" %} + The maximum number of conversation parts that can be returned via the API is 500. If you have more than that we will return the 500 most recent conversation parts. + {% /admonition %} + + For AI agent conversation metadata, please note that you need to have the agent enabled in your workspace, which is a [paid feature](https://www.intercom.com/help/en/articles/8205718-fin-resolutions#h_97f8c2e671). + + Parameters + ---------- + conversation_id : str + The id of the conversation to target + + display_as : typing.Optional[str] + Set to plaintext to retrieve conversation messages in plain text. + + include_translations : typing.Optional[bool] + If set to true, conversation parts will be translated to the detected language of the conversation. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + conversation found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.conversations.find( + conversation_id="123", + display_as="plaintext", + include_translations=True, + ) + """ + _response = self._raw_client.find( + conversation_id, + display_as=display_as, + include_translations=include_translations, + request_options=request_options, + ) + return _response.data + + def update( + self, + conversation_id: str, + *, + display_as: typing.Optional[str] = None, + read: typing.Optional[bool] = OMIT, + title: typing.Optional[str] = OMIT, + custom_attributes: typing.Optional[CustomAttributes] = OMIT, + company_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + + You can update an existing conversation. + + {% admonition type="info" name="Replying and other actions" %} + If you want to reply to a coveration or take an action such as assign, unassign, open, close or snooze, take a look at the reply and manage endpoints. + {% /admonition %} + + {% admonition type="info" %} + This endpoint handles both **conversation updates** and **custom object associations**. + + See _`update a conversation with an association to a custom object instance`_ in the request/response examples to see the custom object association format. + {% /admonition %} + + Parameters + ---------- + conversation_id : str + The id of the conversation to target + + display_as : typing.Optional[str] + Set to plaintext to retrieve conversation messages in plain text. + + read : typing.Optional[bool] + Mark a conversation as read within Intercom. + + title : typing.Optional[str] + The title given to the conversation + + custom_attributes : typing.Optional[CustomAttributes] + + company_id : typing.Optional[str] + The ID of the company that the conversation is associated with. The unique identifier for the company which is given by Intercom. Set to nil to remove company. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + update a conversation with an association to a custom object instance + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.conversations.update( + conversation_id="conversation_id", + display_as="plaintext", + read=True, + title="new conversation title", + custom_attributes={"issue_type": "Billing", "priority": "High"}, + ) + """ + _response = self._raw_client.update( + conversation_id, + display_as=display_as, + read=read, + title=title, + custom_attributes=custom_attributes, + company_id=company_id, + request_options=request_options, + ) + return _response.data + + def delete_conversation( + self, conversation_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> ConversationDeleted: + """ + You can delete a single conversation. + + Parameters + ---------- + conversation_id : int + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ConversationDeleted + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.conversations.delete_conversation( + conversation_id=1, + ) + """ + _response = self._raw_client.delete_conversation(conversation_id, request_options=request_options) + return _response.data + + def search( + self, + *, + query: SearchRequestQuery, + pagination: typing.Optional[StartingAfterPaging] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Conversation, ConversationList]: + """ + You can search for multiple conversations by the value of their attributes in order to fetch exactly which ones you want. + + To search for conversations, you need to send a `POST` request to `https://api.intercom.io/conversations/search`. + + This will accept a query object in the body which will define your filters in order to search for conversations. + {% 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 `20` results per page and maximum is `150`. + 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 %} + + ### 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 + + ### Accepted Fields + + Most keys listed in the conversation 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"`). + The `source.body` field is unique as the search will not be performed against the entire value, but instead against every element of the value separately. For example, when searching for a conversation with a `"I need support"` body - the query should contain a `=` operator with the value `"support"` for such conversation to be returned. A query with a `=` operator and a `"need support"` value will not yield a result. + + | Field | Type | + | :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | + | id | String | + | created_at | Date (UNIX timestamp) | + | updated_at | Date (UNIX timestamp) | + | source.type | String
Accepted fields are `conversation`, `email`, `facebook`, `instagram`, `phone_call`, `phone_switch`, `push`, `sms`, `twitter` and `whatsapp`. | + | source.id | String | + | source.delivered_as | String | + | source.subject | String | + | source.body | String | + | source.author.id | String | + | source.author.type | String | + | source.author.name | String | + | source.author.email | String | + | source.url | String | + | contact_ids | String | + | teammate_ids | String | + | admin_assignee_id | String | + | team_assignee_id | String | + | channel_initiated | String | + | open | Boolean | + | read | Boolean | + | state | String | + | waiting_since | Date (UNIX timestamp) | + | snoozed_until | Date (UNIX timestamp) | + | tag_ids | String | + | priority | String | + | statistics.time_to_assignment | Integer | + | statistics.time_to_admin_reply | Integer | + | statistics.time_to_first_close | Integer | + | statistics.time_to_last_close | Integer | + | statistics.median_time_to_reply | Integer | + | statistics.first_contact_reply_at | Date (UNIX timestamp) | + | statistics.first_assignment_at | Date (UNIX timestamp) | + | statistics.first_admin_reply_at | Date (UNIX timestamp) | + | statistics.first_close_at | Date (UNIX timestamp) | + | statistics.last_assignment_at | Date (UNIX timestamp) | + | statistics.last_assignment_admin_reply_at | Date (UNIX timestamp) | + | statistics.last_contact_reply_at | Date (UNIX timestamp) | + | statistics.last_admin_reply_at | Date (UNIX timestamp) | + | statistics.last_close_at | Date (UNIX timestamp) | + | statistics.last_closed_by_id | String | + | statistics.count_reopens | Integer | + | statistics.count_assignments | Integer | + | statistics.count_conversation_parts | Integer | + | conversation_rating.requested_at | Date (UNIX timestamp) | + | conversation_rating.replied_at | Date (UNIX timestamp) | + | conversation_rating.score | Integer | + | conversation_rating.remark | String | + | conversation_rating.contact_id | String | + | conversation_rating.admin_d | String | + | ai_agent_participated | Boolean | + | ai_agent.resolution_state | String | + | ai_agent.last_answer_type | String | + | ai_agent.rating | Integer | + | ai_agent.rating_remark | String | + | ai_agent.source_type | String | + | ai_agent.source_title | String | + + ### Accepted Operators + + 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 Shortcut for `OR` queries Values most be in Array | + | NIN | All | Not In Shortcut for `OR !` queries Values must be in Array | + | > | Integer Date (UNIX Timestamp) | Greater (or equal) than | + | < | Integer Date (UNIX Timestamp) | Lower (or equal) 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[Conversation, ConversationList] + successful + + Examples + -------- + from intercom import ( + Intercom, + MultipleFilterSearchRequest, + SingleFilterSearchRequest, + StartingAfterPaging, + ) + + client = Intercom( + token="YOUR_TOKEN", + ) + response = client.conversations.search( + query=MultipleFilterSearchRequest( + operator="AND", + value=[ + SingleFilterSearchRequest( + field="created_at", + operator=">", + value="1306054154", + ) + ], + ), + pagination=StartingAfterPaging( + per_page=5, + ), + ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.search(query=query, pagination=pagination, request_options=request_options) + + def reply( + self, + conversation_id: str, + *, + request: ReplyConversationRequest, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + You can reply to a conversation with a message from an admin or on behalf of a contact, or with a note for admins. + + Parameters + ---------- + conversation_id : str + The Intercom provisioned identifier for the conversation or the string "last" to reply to the last part of the conversation + + request : ReplyConversationRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + User last conversation reply + + Examples + -------- + from intercom import ContactReplyIntercomUserIdRequest, Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.conversations.reply( + conversation_id='123 or "last"', + request=ContactReplyIntercomUserIdRequest( + body="Thanks again :)", + intercom_user_id="6762f1571bb69f9f2193bbbb", + ), + ) + """ + _response = self._raw_client.reply(conversation_id, request=request, request_options=request_options) + return _response.data + + def manage( + self, + conversation_id: str, + *, + request: ConversationsManageRequestBody, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + For managing conversations you can: + - Close a conversation + - Snooze a conversation to reopen on a future date + - Open a conversation which is `snoozed` or `closed` + - Assign a conversation to an admin and/or team. + + Parameters + ---------- + conversation_id : str + The identifier for the conversation as given by Intercom. + + request : ConversationsManageRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + Assign a conversation + + Examples + -------- + from intercom import Intercom + from intercom.conversations import ConversationsManageRequestBody_Close + + client = Intercom( + token="YOUR_TOKEN", + ) + client.conversations.manage( + conversation_id="123", + request=ConversationsManageRequestBody_Close( + admin_id="12345", + ), + ) + """ + _response = self._raw_client.manage(conversation_id, request=request, request_options=request_options) + return _response.data + + def attach_contact_as_admin( + self, + conversation_id: str, + *, + admin_id: typing.Optional[str] = OMIT, + customer: typing.Optional[AttachContactToConversationRequestCustomer] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + You can add participants who are contacts to a conversation, on behalf of either another contact or an admin. + + {% admonition type="warning" name="Contacts without an email" %} + If you add a contact via the email parameter and there is no user/lead found on that workspace with he given email, then we will create a new contact with `role` set to `lead`. + {% /admonition %} + + Parameters + ---------- + conversation_id : str + The identifier for the conversation as given by Intercom. + + admin_id : typing.Optional[str] + The `id` of the admin who is adding the new participant. + + customer : typing.Optional[AttachContactToConversationRequestCustomer] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + Attach a contact to a conversation + + Examples + -------- + from intercom import Intercom + from intercom.conversations import ( + AttachContactToConversationRequestCustomerIntercomUserId, + ) + + client = Intercom( + token="YOUR_TOKEN", + ) + client.conversations.attach_contact_as_admin( + conversation_id="123", + admin_id="12345", + customer=AttachContactToConversationRequestCustomerIntercomUserId( + intercom_user_id="6762f19b1bb69f9f2193bbd4", + ), + ) + """ + _response = self._raw_client.attach_contact_as_admin( + conversation_id, admin_id=admin_id, customer=customer, request_options=request_options + ) + return _response.data + + def detach_contact_as_admin( + self, + conversation_id: str, + contact_id: str, + *, + admin_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + You can add participants who are contacts to a conversation, on behalf of either another contact or an admin. + + {% admonition type="warning" name="Contacts without an email" %} + If you add a contact via the email parameter and there is no user/lead found on that workspace with he given email, then we will create a new contact with `role` set to `lead`. + {% /admonition %} + + Parameters + ---------- + conversation_id : str + The identifier for the conversation as given by Intercom. + + contact_id : str + The identifier for the contact as given by Intercom. + + admin_id : str + The `id` of the admin who is performing the action. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + Detach a contact from a group conversation + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.conversations.detach_contact_as_admin( + conversation_id="123", + contact_id="123", + admin_id="5017690", + ) + """ + _response = self._raw_client.detach_contact_as_admin( + conversation_id, contact_id, admin_id=admin_id, request_options=request_options + ) + return _response.data + + def redact_conversation_part( + self, *, request: RedactConversationRequest, request_options: typing.Optional[RequestOptions] = None + ) -> Conversation: + """ + You can redact a conversation part or the source message of a conversation (as seen in the source object). + + {% admonition type="info" name="Redacting parts and messages" %} + If you are redacting a conversation part, it must have a `body`. If you are redacting a source message, it must have been created by a contact. We will return a `conversation_part_not_redactable` error if these criteria are not met. + {% /admonition %} + + Parameters + ---------- + request : RedactConversationRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + Redact a conversation part + + Examples + -------- + from intercom import Intercom, RedactConversationRequest_ConversationPart + + client = Intercom( + token="YOUR_TOKEN", + ) + client.conversations.redact_conversation_part( + request=RedactConversationRequest_ConversationPart( + conversation_id="19894788788", + conversation_part_id="19381789428", + ), + ) + """ + _response = self._raw_client.redact_conversation_part(request=request, request_options=request_options) + return _response.data + + def convert_to_ticket( + self, + conversation_id: int, + *, + ticket_type_id: str, + attributes: typing.Optional[TicketRequestCustomAttributes] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[Ticket]: + """ + You can convert a conversation to a ticket. + + Parameters + ---------- + conversation_id : int + The id of the conversation to target + + ticket_type_id : str + The ID of the type of ticket you want to convert the conversation to + + attributes : typing.Optional[TicketRequestCustomAttributes] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Ticket] + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.conversations.convert_to_ticket( + conversation_id=1, + ticket_type_id="53", + ) + """ + _response = self._raw_client.convert_to_ticket( + conversation_id, ticket_type_id=ticket_type_id, attributes=attributes, request_options=request_options + ) + return _response.data + + def run_assignment_rules( + self, conversation_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Conversation: + """ + {% admonition type="danger" name="Deprecation of Run Assignment Rules" %} + Run assignment rules is now deprecated in version 2.12 and future versions and will be permanently removed on December 31, 2026. After this date, any requests made to this endpoint will fail. + {% /admonition %} + You can let a conversation be automatically assigned following assignment rules. + {% admonition type="warning" name="When using workflows" %} + It is not possible to use this endpoint with Workflows. + {% /admonition %} + + Parameters + ---------- + conversation_id : str + The identifier for the conversation as given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + Assign a conversation using assignment rules + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.conversations.run_assignment_rules( + conversation_id="123", + ) + """ + _response = self._raw_client.run_assignment_rules(conversation_id, request_options=request_options) + return _response.data + + +class AsyncConversationsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawConversationsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawConversationsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawConversationsClient + """ + return self._raw_client + + async def list( + self, + *, + per_page: typing.Optional[int] = None, + starting_after: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[Conversation, ConversationList]: + """ + You can fetch a list of all conversations. + + You can optionally request the result page size and the cursor to start after to fetch the result. + {% admonition type="warning" name="Pagination" %} + You can use pagination to limit the number of results returned. The default is `20` 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 + ---------- + per_page : typing.Optional[int] + How many results per page + + starting_after : typing.Optional[str] + String used to get the next page of conversations. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[Conversation, ConversationList] + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + response = await client.conversations.list( + per_page=1, + starting_after="starting_after", + ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page + + + asyncio.run(main()) + """ + return await self._raw_client.list( + per_page=per_page, starting_after=starting_after, request_options=request_options + ) + + async def create( + self, + *, + from_: CreateConversationRequestFrom, + body: str, + created_at: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Message: + """ + You can create a conversation that has been initiated by a contact (ie. user or lead). + The conversation can be an in-app message only. + + {% admonition type="info" name="Sending for visitors" %} + You can also send a message from a visitor by specifying their `user_id` or `id` value in the `from` field, along with a `type` field value of `contact`. + This visitor will be automatically converted to a contact with a lead role once the conversation is created. + {% /admonition %} + + This will return the Message model that has been created. + + Parameters + ---------- + from_ : CreateConversationRequestFrom + + body : str + The content of the message. HTML is not supported. + + created_at : typing.Optional[int] + The time the conversation was created as a UTC Unix timestamp. If not provided, the current time will be used. This field is only recommneded for migrating past conversations from another source into Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Message + conversation created + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.conversations import CreateConversationRequestFrom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.conversations.create( + from_=CreateConversationRequestFrom( + type="user", + id="6762f11b1bb69f9f2193bba3", + ), + body="Hello there", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + from_=from_, body=body, created_at=created_at, request_options=request_options + ) + return _response.data + + async def find( + self, + conversation_id: str, + *, + display_as: typing.Optional[str] = None, + include_translations: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + + You can fetch the details of a single conversation. + + This will return a single Conversation model with all its conversation parts. + + {% admonition type="warning" name="Hard limit of 500 parts" %} + The maximum number of conversation parts that can be returned via the API is 500. If you have more than that we will return the 500 most recent conversation parts. + {% /admonition %} + + For AI agent conversation metadata, please note that you need to have the agent enabled in your workspace, which is a [paid feature](https://www.intercom.com/help/en/articles/8205718-fin-resolutions#h_97f8c2e671). + + Parameters + ---------- + conversation_id : str + The id of the conversation to target + + display_as : typing.Optional[str] + Set to plaintext to retrieve conversation messages in plain text. + + include_translations : typing.Optional[bool] + If set to true, conversation parts will be translated to the detected language of the conversation. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + conversation found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.conversations.find( + conversation_id="123", + display_as="plaintext", + include_translations=True, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.find( + conversation_id, + display_as=display_as, + include_translations=include_translations, + request_options=request_options, + ) + return _response.data + + async def update( + self, + conversation_id: str, + *, + display_as: typing.Optional[str] = None, + read: typing.Optional[bool] = OMIT, + title: typing.Optional[str] = OMIT, + custom_attributes: typing.Optional[CustomAttributes] = OMIT, + company_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + + You can update an existing conversation. + + {% admonition type="info" name="Replying and other actions" %} + If you want to reply to a coveration or take an action such as assign, unassign, open, close or snooze, take a look at the reply and manage endpoints. + {% /admonition %} + + {% admonition type="info" %} + This endpoint handles both **conversation updates** and **custom object associations**. + + See _`update a conversation with an association to a custom object instance`_ in the request/response examples to see the custom object association format. + {% /admonition %} + + Parameters + ---------- + conversation_id : str + The id of the conversation to target + + display_as : typing.Optional[str] + Set to plaintext to retrieve conversation messages in plain text. + + read : typing.Optional[bool] + Mark a conversation as read within Intercom. + + title : typing.Optional[str] + The title given to the conversation + + custom_attributes : typing.Optional[CustomAttributes] + + company_id : typing.Optional[str] + The ID of the company that the conversation is associated with. The unique identifier for the company which is given by Intercom. Set to nil to remove company. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + update a conversation with an association to a custom object instance + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.conversations.update( + conversation_id="conversation_id", + display_as="plaintext", + read=True, + title="new conversation title", + custom_attributes={"issue_type": "Billing", "priority": "High"}, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update( + conversation_id, + display_as=display_as, + read=read, + title=title, + custom_attributes=custom_attributes, + company_id=company_id, + request_options=request_options, + ) + return _response.data + + async def delete_conversation( + self, conversation_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> ConversationDeleted: + """ + You can delete a single conversation. + + Parameters + ---------- + conversation_id : int + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ConversationDeleted + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.conversations.delete_conversation( + conversation_id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_conversation(conversation_id, request_options=request_options) + return _response.data + + async def search( + self, + *, + query: SearchRequestQuery, + pagination: typing.Optional[StartingAfterPaging] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[Conversation, ConversationList]: + """ + You can search for multiple conversations by the value of their attributes in order to fetch exactly which ones you want. + + To search for conversations, you need to send a `POST` request to `https://api.intercom.io/conversations/search`. + + This will accept a query object in the body which will define your filters in order to search for conversations. + {% 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 `20` results per page and maximum is `150`. + 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 %} + + ### 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 + + ### Accepted Fields + + Most keys listed in the conversation 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"`). + The `source.body` field is unique as the search will not be performed against the entire value, but instead against every element of the value separately. For example, when searching for a conversation with a `"I need support"` body - the query should contain a `=` operator with the value `"support"` for such conversation to be returned. A query with a `=` operator and a `"need support"` value will not yield a result. + + | Field | Type | + | :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | + | id | String | + | created_at | Date (UNIX timestamp) | + | updated_at | Date (UNIX timestamp) | + | source.type | String
Accepted fields are `conversation`, `email`, `facebook`, `instagram`, `phone_call`, `phone_switch`, `push`, `sms`, `twitter` and `whatsapp`. | + | source.id | String | + | source.delivered_as | String | + | source.subject | String | + | source.body | String | + | source.author.id | String | + | source.author.type | String | + | source.author.name | String | + | source.author.email | String | + | source.url | String | + | contact_ids | String | + | teammate_ids | String | + | admin_assignee_id | String | + | team_assignee_id | String | + | channel_initiated | String | + | open | Boolean | + | read | Boolean | + | state | String | + | waiting_since | Date (UNIX timestamp) | + | snoozed_until | Date (UNIX timestamp) | + | tag_ids | String | + | priority | String | + | statistics.time_to_assignment | Integer | + | statistics.time_to_admin_reply | Integer | + | statistics.time_to_first_close | Integer | + | statistics.time_to_last_close | Integer | + | statistics.median_time_to_reply | Integer | + | statistics.first_contact_reply_at | Date (UNIX timestamp) | + | statistics.first_assignment_at | Date (UNIX timestamp) | + | statistics.first_admin_reply_at | Date (UNIX timestamp) | + | statistics.first_close_at | Date (UNIX timestamp) | + | statistics.last_assignment_at | Date (UNIX timestamp) | + | statistics.last_assignment_admin_reply_at | Date (UNIX timestamp) | + | statistics.last_contact_reply_at | Date (UNIX timestamp) | + | statistics.last_admin_reply_at | Date (UNIX timestamp) | + | statistics.last_close_at | Date (UNIX timestamp) | + | statistics.last_closed_by_id | String | + | statistics.count_reopens | Integer | + | statistics.count_assignments | Integer | + | statistics.count_conversation_parts | Integer | + | conversation_rating.requested_at | Date (UNIX timestamp) | + | conversation_rating.replied_at | Date (UNIX timestamp) | + | conversation_rating.score | Integer | + | conversation_rating.remark | String | + | conversation_rating.contact_id | String | + | conversation_rating.admin_d | String | + | ai_agent_participated | Boolean | + | ai_agent.resolution_state | String | + | ai_agent.last_answer_type | String | + | ai_agent.rating | Integer | + | ai_agent.rating_remark | String | + | ai_agent.source_type | String | + | ai_agent.source_title | String | + + ### Accepted Operators + + 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 Shortcut for `OR` queries Values most be in Array | + | NIN | All | Not In Shortcut for `OR !` queries Values must be in Array | + | > | Integer Date (UNIX Timestamp) | Greater (or equal) than | + | < | Integer Date (UNIX Timestamp) | Lower (or equal) 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[Conversation, ConversationList] + successful + + Examples + -------- + import asyncio + + from intercom import ( + AsyncIntercom, + MultipleFilterSearchRequest, + SingleFilterSearchRequest, + StartingAfterPaging, + ) + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + response = await client.conversations.search( + query=MultipleFilterSearchRequest( + operator="AND", + value=[ + SingleFilterSearchRequest( + field="created_at", + operator=">", + value="1306054154", + ) + ], + ), + pagination=StartingAfterPaging( + per_page=5, + ), + ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page + + + asyncio.run(main()) + """ + return await self._raw_client.search(query=query, pagination=pagination, request_options=request_options) + + async def reply( + self, + conversation_id: str, + *, + request: ReplyConversationRequest, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + You can reply to a conversation with a message from an admin or on behalf of a contact, or with a note for admins. + + Parameters + ---------- + conversation_id : str + The Intercom provisioned identifier for the conversation or the string "last" to reply to the last part of the conversation + + request : ReplyConversationRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + User last conversation reply + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom, ContactReplyIntercomUserIdRequest + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.conversations.reply( + conversation_id='123 or "last"', + request=ContactReplyIntercomUserIdRequest( + body="Thanks again :)", + intercom_user_id="6762f1571bb69f9f2193bbbb", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.reply(conversation_id, request=request, request_options=request_options) + return _response.data + + async def manage( + self, + conversation_id: str, + *, + request: ConversationsManageRequestBody, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + For managing conversations you can: + - Close a conversation + - Snooze a conversation to reopen on a future date + - Open a conversation which is `snoozed` or `closed` + - Assign a conversation to an admin and/or team. + + Parameters + ---------- + conversation_id : str + The identifier for the conversation as given by Intercom. + + request : ConversationsManageRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + Assign a conversation + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.conversations import ConversationsManageRequestBody_Close + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.conversations.manage( + conversation_id="123", + request=ConversationsManageRequestBody_Close( + admin_id="12345", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.manage(conversation_id, request=request, request_options=request_options) + return _response.data + + async def attach_contact_as_admin( + self, + conversation_id: str, + *, + admin_id: typing.Optional[str] = OMIT, + customer: typing.Optional[AttachContactToConversationRequestCustomer] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + You can add participants who are contacts to a conversation, on behalf of either another contact or an admin. + + {% admonition type="warning" name="Contacts without an email" %} + If you add a contact via the email parameter and there is no user/lead found on that workspace with he given email, then we will create a new contact with `role` set to `lead`. + {% /admonition %} + + Parameters + ---------- + conversation_id : str + The identifier for the conversation as given by Intercom. + + admin_id : typing.Optional[str] + The `id` of the admin who is adding the new participant. + + customer : typing.Optional[AttachContactToConversationRequestCustomer] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + Attach a contact to a conversation + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.conversations import ( + AttachContactToConversationRequestCustomerIntercomUserId, + ) + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.conversations.attach_contact_as_admin( + conversation_id="123", + admin_id="12345", + customer=AttachContactToConversationRequestCustomerIntercomUserId( + intercom_user_id="6762f19b1bb69f9f2193bbd4", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.attach_contact_as_admin( + conversation_id, admin_id=admin_id, customer=customer, request_options=request_options + ) + return _response.data + + async def detach_contact_as_admin( + self, + conversation_id: str, + contact_id: str, + *, + admin_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + You can add participants who are contacts to a conversation, on behalf of either another contact or an admin. + + {% admonition type="warning" name="Contacts without an email" %} + If you add a contact via the email parameter and there is no user/lead found on that workspace with he given email, then we will create a new contact with `role` set to `lead`. + {% /admonition %} + + Parameters + ---------- + conversation_id : str + The identifier for the conversation as given by Intercom. + + contact_id : str + The identifier for the contact as given by Intercom. + + admin_id : str + The `id` of the admin who is performing the action. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + Detach a contact from a group conversation + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.conversations.detach_contact_as_admin( + conversation_id="123", + contact_id="123", + admin_id="5017690", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.detach_contact_as_admin( + conversation_id, contact_id, admin_id=admin_id, request_options=request_options + ) + return _response.data + + async def redact_conversation_part( + self, *, request: RedactConversationRequest, request_options: typing.Optional[RequestOptions] = None + ) -> Conversation: + """ + You can redact a conversation part or the source message of a conversation (as seen in the source object). + + {% admonition type="info" name="Redacting parts and messages" %} + If you are redacting a conversation part, it must have a `body`. If you are redacting a source message, it must have been created by a contact. We will return a `conversation_part_not_redactable` error if these criteria are not met. + {% /admonition %} + + Parameters + ---------- + request : RedactConversationRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + Redact a conversation part + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom, RedactConversationRequest_ConversationPart + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.conversations.redact_conversation_part( + request=RedactConversationRequest_ConversationPart( + conversation_id="19894788788", + conversation_part_id="19381789428", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.redact_conversation_part(request=request, request_options=request_options) + return _response.data + + async def convert_to_ticket( + self, + conversation_id: int, + *, + ticket_type_id: str, + attributes: typing.Optional[TicketRequestCustomAttributes] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[Ticket]: + """ + You can convert a conversation to a ticket. + + Parameters + ---------- + conversation_id : int + The id of the conversation to target + + ticket_type_id : str + The ID of the type of ticket you want to convert the conversation to + + attributes : typing.Optional[TicketRequestCustomAttributes] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Ticket] + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.conversations.convert_to_ticket( + conversation_id=1, + ticket_type_id="53", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.convert_to_ticket( + conversation_id, ticket_type_id=ticket_type_id, attributes=attributes, request_options=request_options + ) + return _response.data + + async def run_assignment_rules( + self, conversation_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Conversation: + """ + {% admonition type="danger" name="Deprecation of Run Assignment Rules" %} + Run assignment rules is now deprecated in version 2.12 and future versions and will be permanently removed on December 31, 2026. After this date, any requests made to this endpoint will fail. + {% /admonition %} + You can let a conversation be automatically assigned following assignment rules. + {% admonition type="warning" name="When using workflows" %} + It is not possible to use this endpoint with Workflows. + {% /admonition %} + + Parameters + ---------- + conversation_id : str + The identifier for the conversation as given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + Assign a conversation using assignment rules + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.conversations.run_assignment_rules( + conversation_id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.run_assignment_rules(conversation_id, request_options=request_options) + return _response.data diff --git a/src/intercom/conversations/raw_client.py b/src/intercom/conversations/raw_client.py new file mode 100644 index 00000000..25e74a0c --- /dev/null +++ b/src/intercom/conversations/raw_client.py @@ -0,0 +1,2504 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.bad_request_error import BadRequestError +from ..errors.forbidden_error import ForbiddenError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..errors.unprocessable_entity_error import UnprocessableEntityError +from ..messages.types.message import Message +from ..tickets.types.ticket import Ticket +from ..types.conversation_deleted import ConversationDeleted +from ..types.conversation_list import ConversationList +from ..types.custom_attributes import CustomAttributes +from ..types.error import Error +from ..types.redact_conversation_request import RedactConversationRequest +from ..types.reply_conversation_request import ReplyConversationRequest +from ..types.search_request_query import SearchRequestQuery +from ..types.starting_after_paging import StartingAfterPaging +from ..types.ticket_request_custom_attributes import TicketRequestCustomAttributes +from .types.attach_contact_to_conversation_request_customer import AttachContactToConversationRequestCustomer +from .types.conversation import Conversation +from .types.conversations_manage_request_body import ConversationsManageRequestBody +from .types.create_conversation_request_from import CreateConversationRequestFrom + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawConversationsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list( + self, + *, + per_page: typing.Optional[int] = None, + starting_after: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Conversation, ConversationList]: + """ + You can fetch a list of all conversations. + + You can optionally request the result page size and the cursor to start after to fetch the result. + {% admonition type="warning" name="Pagination" %} + You can use pagination to limit the number of results returned. The default is `20` 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 + ---------- + per_page : typing.Optional[int] + How many results per page + + starting_after : typing.Optional[str] + String used to get the next page of conversations. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[Conversation, ConversationList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "conversations", + method="GET", + params={ + "per_page": per_page, + "starting_after": starting_after, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + ConversationList, + construct_type( + type_=ConversationList, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.conversations + _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( + 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(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + 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, + *, + from_: CreateConversationRequestFrom, + body: str, + created_at: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Message]: + """ + You can create a conversation that has been initiated by a contact (ie. user or lead). + The conversation can be an in-app message only. + + {% admonition type="info" name="Sending for visitors" %} + You can also send a message from a visitor by specifying their `user_id` or `id` value in the `from` field, along with a `type` field value of `contact`. + This visitor will be automatically converted to a contact with a lead role once the conversation is created. + {% /admonition %} + + This will return the Message model that has been created. + + Parameters + ---------- + from_ : CreateConversationRequestFrom + + body : str + The content of the message. HTML is not supported. + + created_at : typing.Optional[int] + The time the conversation was created as a UTC Unix timestamp. If not provided, the current time will be used. This field is only recommneded for migrating past conversations from another source into Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Message] + conversation created + """ + _response = self._client_wrapper.httpx_client.request( + "conversations", + method="POST", + json={ + "from": convert_and_respect_annotation_metadata( + object_=from_, annotation=CreateConversationRequestFrom, direction="write" + ), + "body": body, + "created_at": created_at, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Message, + construct_type( + type_=Message, # 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 == 403: + raise ForbiddenError( + 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, + conversation_id: str, + *, + display_as: typing.Optional[str] = None, + include_translations: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Conversation]: + """ + + You can fetch the details of a single conversation. + + This will return a single Conversation model with all its conversation parts. + + {% admonition type="warning" name="Hard limit of 500 parts" %} + The maximum number of conversation parts that can be returned via the API is 500. If you have more than that we will return the 500 most recent conversation parts. + {% /admonition %} + + For AI agent conversation metadata, please note that you need to have the agent enabled in your workspace, which is a [paid feature](https://www.intercom.com/help/en/articles/8205718-fin-resolutions#h_97f8c2e671). + + Parameters + ---------- + conversation_id : str + The id of the conversation to target + + display_as : typing.Optional[str] + Set to plaintext to retrieve conversation messages in plain text. + + include_translations : typing.Optional[bool] + If set to true, conversation parts will be translated to the detected language of the conversation. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Conversation] + conversation found + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}", + method="GET", + params={ + "display_as": display_as, + "include_translations": include_translations, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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 update( + self, + conversation_id: str, + *, + display_as: typing.Optional[str] = None, + read: typing.Optional[bool] = OMIT, + title: typing.Optional[str] = OMIT, + custom_attributes: typing.Optional[CustomAttributes] = OMIT, + company_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Conversation]: + """ + + You can update an existing conversation. + + {% admonition type="info" name="Replying and other actions" %} + If you want to reply to a coveration or take an action such as assign, unassign, open, close or snooze, take a look at the reply and manage endpoints. + {% /admonition %} + + {% admonition type="info" %} + This endpoint handles both **conversation updates** and **custom object associations**. + + See _`update a conversation with an association to a custom object instance`_ in the request/response examples to see the custom object association format. + {% /admonition %} + + Parameters + ---------- + conversation_id : str + The id of the conversation to target + + display_as : typing.Optional[str] + Set to plaintext to retrieve conversation messages in plain text. + + read : typing.Optional[bool] + Mark a conversation as read within Intercom. + + title : typing.Optional[str] + The title given to the conversation + + custom_attributes : typing.Optional[CustomAttributes] + + company_id : typing.Optional[str] + The ID of the company that the conversation is associated with. The unique identifier for the company which is given by Intercom. Set to nil to remove company. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Conversation] + update a conversation with an association to a custom object instance + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}", + method="PUT", + params={ + "display_as": display_as, + }, + json={ + "read": read, + "title": title, + "custom_attributes": convert_and_respect_annotation_metadata( + object_=custom_attributes, annotation=CustomAttributes, direction="write" + ), + "company_id": company_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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 delete_conversation( + self, conversation_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ConversationDeleted]: + """ + You can delete a single conversation. + + Parameters + ---------- + conversation_id : int + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ConversationDeleted] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ConversationDeleted, + construct_type( + type_=ConversationDeleted, # 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 == 403: + raise ForbiddenError( + 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[Conversation, ConversationList]: + """ + You can search for multiple conversations by the value of their attributes in order to fetch exactly which ones you want. + + To search for conversations, you need to send a `POST` request to `https://api.intercom.io/conversations/search`. + + This will accept a query object in the body which will define your filters in order to search for conversations. + {% 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 `20` results per page and maximum is `150`. + 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 %} + + ### 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 + + ### Accepted Fields + + Most keys listed in the conversation 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"`). + The `source.body` field is unique as the search will not be performed against the entire value, but instead against every element of the value separately. For example, when searching for a conversation with a `"I need support"` body - the query should contain a `=` operator with the value `"support"` for such conversation to be returned. A query with a `=` operator and a `"need support"` value will not yield a result. + + | Field | Type | + | :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | + | id | String | + | created_at | Date (UNIX timestamp) | + | updated_at | Date (UNIX timestamp) | + | source.type | String
Accepted fields are `conversation`, `email`, `facebook`, `instagram`, `phone_call`, `phone_switch`, `push`, `sms`, `twitter` and `whatsapp`. | + | source.id | String | + | source.delivered_as | String | + | source.subject | String | + | source.body | String | + | source.author.id | String | + | source.author.type | String | + | source.author.name | String | + | source.author.email | String | + | source.url | String | + | contact_ids | String | + | teammate_ids | String | + | admin_assignee_id | String | + | team_assignee_id | String | + | channel_initiated | String | + | open | Boolean | + | read | Boolean | + | state | String | + | waiting_since | Date (UNIX timestamp) | + | snoozed_until | Date (UNIX timestamp) | + | tag_ids | String | + | priority | String | + | statistics.time_to_assignment | Integer | + | statistics.time_to_admin_reply | Integer | + | statistics.time_to_first_close | Integer | + | statistics.time_to_last_close | Integer | + | statistics.median_time_to_reply | Integer | + | statistics.first_contact_reply_at | Date (UNIX timestamp) | + | statistics.first_assignment_at | Date (UNIX timestamp) | + | statistics.first_admin_reply_at | Date (UNIX timestamp) | + | statistics.first_close_at | Date (UNIX timestamp) | + | statistics.last_assignment_at | Date (UNIX timestamp) | + | statistics.last_assignment_admin_reply_at | Date (UNIX timestamp) | + | statistics.last_contact_reply_at | Date (UNIX timestamp) | + | statistics.last_admin_reply_at | Date (UNIX timestamp) | + | statistics.last_close_at | Date (UNIX timestamp) | + | statistics.last_closed_by_id | String | + | statistics.count_reopens | Integer | + | statistics.count_assignments | Integer | + | statistics.count_conversation_parts | Integer | + | conversation_rating.requested_at | Date (UNIX timestamp) | + | conversation_rating.replied_at | Date (UNIX timestamp) | + | conversation_rating.score | Integer | + | conversation_rating.remark | String | + | conversation_rating.contact_id | String | + | conversation_rating.admin_d | String | + | ai_agent_participated | Boolean | + | ai_agent.resolution_state | String | + | ai_agent.last_answer_type | String | + | ai_agent.rating | Integer | + | ai_agent.rating_remark | String | + | ai_agent.source_type | String | + | ai_agent.source_title | String | + + ### Accepted Operators + + 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 Shortcut for `OR` queries Values most be in Array | + | NIN | All | Not In Shortcut for `OR !` queries Values must be in Array | + | > | Integer Date (UNIX Timestamp) | Greater (or equal) than | + | < | Integer Date (UNIX Timestamp) | Lower (or equal) 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[Conversation, ConversationList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "conversations/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( + ConversationList, + construct_type( + type_=ConversationList, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.conversations + _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) + _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 reply( + self, + conversation_id: str, + *, + request: ReplyConversationRequest, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Conversation]: + """ + You can reply to a conversation with a message from an admin or on behalf of a contact, or with a note for admins. + + Parameters + ---------- + conversation_id : str + The Intercom provisioned identifier for the conversation or the string "last" to reply to the last part of the conversation + + request : ReplyConversationRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Conversation] + User last conversation reply + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/reply", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=ReplyConversationRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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 manage( + self, + conversation_id: str, + *, + request: ConversationsManageRequestBody, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Conversation]: + """ + For managing conversations you can: + - Close a conversation + - Snooze a conversation to reopen on a future date + - Open a conversation which is `snoozed` or `closed` + - Assign a conversation to an admin and/or team. + + Parameters + ---------- + conversation_id : str + The identifier for the conversation as given by Intercom. + + request : ConversationsManageRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Conversation] + Assign a conversation + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/parts", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=ConversationsManageRequestBody, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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_contact_as_admin( + self, + conversation_id: str, + *, + admin_id: typing.Optional[str] = OMIT, + customer: typing.Optional[AttachContactToConversationRequestCustomer] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Conversation]: + """ + You can add participants who are contacts to a conversation, on behalf of either another contact or an admin. + + {% admonition type="warning" name="Contacts without an email" %} + If you add a contact via the email parameter and there is no user/lead found on that workspace with he given email, then we will create a new contact with `role` set to `lead`. + {% /admonition %} + + Parameters + ---------- + conversation_id : str + The identifier for the conversation as given by Intercom. + + admin_id : typing.Optional[str] + The `id` of the admin who is adding the new participant. + + customer : typing.Optional[AttachContactToConversationRequestCustomer] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Conversation] + Attach a contact to a conversation + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/customers", + method="POST", + json={ + "admin_id": admin_id, + "customer": convert_and_respect_annotation_metadata( + object_=customer, annotation=AttachContactToConversationRequestCustomer, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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_contact_as_admin( + self, + conversation_id: str, + contact_id: str, + *, + admin_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Conversation]: + """ + You can add participants who are contacts to a conversation, on behalf of either another contact or an admin. + + {% admonition type="warning" name="Contacts without an email" %} + If you add a contact via the email parameter and there is no user/lead found on that workspace with he given email, then we will create a new contact with `role` set to `lead`. + {% /admonition %} + + Parameters + ---------- + conversation_id : str + The identifier for the conversation as given by Intercom. + + contact_id : str + The identifier for the contact as given by Intercom. + + admin_id : str + The `id` of the admin who is performing the action. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Conversation] + Detach a contact from a group conversation + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/customers/{jsonable_encoder(contact_id)}", + method="DELETE", + json={ + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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 redact_conversation_part( + self, *, request: RedactConversationRequest, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Conversation]: + """ + You can redact a conversation part or the source message of a conversation (as seen in the source object). + + {% admonition type="info" name="Redacting parts and messages" %} + If you are redacting a conversation part, it must have a `body`. If you are redacting a source message, it must have been created by a contact. We will return a `conversation_part_not_redactable` error if these criteria are not met. + {% /admonition %} + + Parameters + ---------- + request : RedactConversationRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Conversation] + Redact a conversation part + """ + _response = self._client_wrapper.httpx_client.request( + "conversations/redact", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=RedactConversationRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 convert_to_ticket( + self, + conversation_id: int, + *, + ticket_type_id: str, + attributes: typing.Optional[TicketRequestCustomAttributes] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[typing.Optional[Ticket]]: + """ + You can convert a conversation to a ticket. + + Parameters + ---------- + conversation_id : int + The id of the conversation to target + + ticket_type_id : str + The ID of the type of ticket you want to convert the conversation to + + attributes : typing.Optional[TicketRequestCustomAttributes] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[Ticket]] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/convert", + method="POST", + json={ + "ticket_type_id": ticket_type_id, + "attributes": attributes, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Ticket], + construct_type( + type_=typing.Optional[Ticket], # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + 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 run_assignment_rules( + self, conversation_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Conversation]: + """ + {% admonition type="danger" name="Deprecation of Run Assignment Rules" %} + Run assignment rules is now deprecated in version 2.12 and future versions and will be permanently removed on December 31, 2026. After this date, any requests made to this endpoint will fail. + {% /admonition %} + You can let a conversation be automatically assigned following assignment rules. + {% admonition type="warning" name="When using workflows" %} + It is not possible to use this endpoint with Workflows. + {% /admonition %} + + Parameters + ---------- + conversation_id : str + The identifier for the conversation as given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Conversation] + Assign a conversation using assignment rules + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/run_assignment_rules", + method="POST", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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) + + +class AsyncRawConversationsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, + *, + per_page: typing.Optional[int] = None, + starting_after: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[Conversation, ConversationList]: + """ + You can fetch a list of all conversations. + + You can optionally request the result page size and the cursor to start after to fetch the result. + {% admonition type="warning" name="Pagination" %} + You can use pagination to limit the number of results returned. The default is `20` 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 + ---------- + per_page : typing.Optional[int] + How many results per page + + starting_after : typing.Optional[str] + String used to get the next page of conversations. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[Conversation, ConversationList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "conversations", + method="GET", + params={ + "per_page": per_page, + "starting_after": starting_after, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + ConversationList, + construct_type( + type_=ConversationList, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.conversations + _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( + 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(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + 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, + *, + from_: CreateConversationRequestFrom, + body: str, + created_at: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Message]: + """ + You can create a conversation that has been initiated by a contact (ie. user or lead). + The conversation can be an in-app message only. + + {% admonition type="info" name="Sending for visitors" %} + You can also send a message from a visitor by specifying their `user_id` or `id` value in the `from` field, along with a `type` field value of `contact`. + This visitor will be automatically converted to a contact with a lead role once the conversation is created. + {% /admonition %} + + This will return the Message model that has been created. + + Parameters + ---------- + from_ : CreateConversationRequestFrom + + body : str + The content of the message. HTML is not supported. + + created_at : typing.Optional[int] + The time the conversation was created as a UTC Unix timestamp. If not provided, the current time will be used. This field is only recommneded for migrating past conversations from another source into Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Message] + conversation created + """ + _response = await self._client_wrapper.httpx_client.request( + "conversations", + method="POST", + json={ + "from": convert_and_respect_annotation_metadata( + object_=from_, annotation=CreateConversationRequestFrom, direction="write" + ), + "body": body, + "created_at": created_at, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Message, + construct_type( + type_=Message, # 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 == 403: + raise ForbiddenError( + 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, + conversation_id: str, + *, + display_as: typing.Optional[str] = None, + include_translations: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Conversation]: + """ + + You can fetch the details of a single conversation. + + This will return a single Conversation model with all its conversation parts. + + {% admonition type="warning" name="Hard limit of 500 parts" %} + The maximum number of conversation parts that can be returned via the API is 500. If you have more than that we will return the 500 most recent conversation parts. + {% /admonition %} + + For AI agent conversation metadata, please note that you need to have the agent enabled in your workspace, which is a [paid feature](https://www.intercom.com/help/en/articles/8205718-fin-resolutions#h_97f8c2e671). + + Parameters + ---------- + conversation_id : str + The id of the conversation to target + + display_as : typing.Optional[str] + Set to plaintext to retrieve conversation messages in plain text. + + include_translations : typing.Optional[bool] + If set to true, conversation parts will be translated to the detected language of the conversation. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Conversation] + conversation found + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}", + method="GET", + params={ + "display_as": display_as, + "include_translations": include_translations, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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 update( + self, + conversation_id: str, + *, + display_as: typing.Optional[str] = None, + read: typing.Optional[bool] = OMIT, + title: typing.Optional[str] = OMIT, + custom_attributes: typing.Optional[CustomAttributes] = OMIT, + company_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Conversation]: + """ + + You can update an existing conversation. + + {% admonition type="info" name="Replying and other actions" %} + If you want to reply to a coveration or take an action such as assign, unassign, open, close or snooze, take a look at the reply and manage endpoints. + {% /admonition %} + + {% admonition type="info" %} + This endpoint handles both **conversation updates** and **custom object associations**. + + See _`update a conversation with an association to a custom object instance`_ in the request/response examples to see the custom object association format. + {% /admonition %} + + Parameters + ---------- + conversation_id : str + The id of the conversation to target + + display_as : typing.Optional[str] + Set to plaintext to retrieve conversation messages in plain text. + + read : typing.Optional[bool] + Mark a conversation as read within Intercom. + + title : typing.Optional[str] + The title given to the conversation + + custom_attributes : typing.Optional[CustomAttributes] + + company_id : typing.Optional[str] + The ID of the company that the conversation is associated with. The unique identifier for the company which is given by Intercom. Set to nil to remove company. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Conversation] + update a conversation with an association to a custom object instance + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}", + method="PUT", + params={ + "display_as": display_as, + }, + json={ + "read": read, + "title": title, + "custom_attributes": convert_and_respect_annotation_metadata( + object_=custom_attributes, annotation=CustomAttributes, direction="write" + ), + "company_id": company_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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 delete_conversation( + self, conversation_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ConversationDeleted]: + """ + You can delete a single conversation. + + Parameters + ---------- + conversation_id : int + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ConversationDeleted] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ConversationDeleted, + construct_type( + type_=ConversationDeleted, # 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 == 403: + raise ForbiddenError( + 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[Conversation, ConversationList]: + """ + You can search for multiple conversations by the value of their attributes in order to fetch exactly which ones you want. + + To search for conversations, you need to send a `POST` request to `https://api.intercom.io/conversations/search`. + + This will accept a query object in the body which will define your filters in order to search for conversations. + {% 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 `20` results per page and maximum is `150`. + 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 %} + + ### 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 + + ### Accepted Fields + + Most keys listed in the conversation 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"`). + The `source.body` field is unique as the search will not be performed against the entire value, but instead against every element of the value separately. For example, when searching for a conversation with a `"I need support"` body - the query should contain a `=` operator with the value `"support"` for such conversation to be returned. A query with a `=` operator and a `"need support"` value will not yield a result. + + | Field | Type | + | :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | + | id | String | + | created_at | Date (UNIX timestamp) | + | updated_at | Date (UNIX timestamp) | + | source.type | String
Accepted fields are `conversation`, `email`, `facebook`, `instagram`, `phone_call`, `phone_switch`, `push`, `sms`, `twitter` and `whatsapp`. | + | source.id | String | + | source.delivered_as | String | + | source.subject | String | + | source.body | String | + | source.author.id | String | + | source.author.type | String | + | source.author.name | String | + | source.author.email | String | + | source.url | String | + | contact_ids | String | + | teammate_ids | String | + | admin_assignee_id | String | + | team_assignee_id | String | + | channel_initiated | String | + | open | Boolean | + | read | Boolean | + | state | String | + | waiting_since | Date (UNIX timestamp) | + | snoozed_until | Date (UNIX timestamp) | + | tag_ids | String | + | priority | String | + | statistics.time_to_assignment | Integer | + | statistics.time_to_admin_reply | Integer | + | statistics.time_to_first_close | Integer | + | statistics.time_to_last_close | Integer | + | statistics.median_time_to_reply | Integer | + | statistics.first_contact_reply_at | Date (UNIX timestamp) | + | statistics.first_assignment_at | Date (UNIX timestamp) | + | statistics.first_admin_reply_at | Date (UNIX timestamp) | + | statistics.first_close_at | Date (UNIX timestamp) | + | statistics.last_assignment_at | Date (UNIX timestamp) | + | statistics.last_assignment_admin_reply_at | Date (UNIX timestamp) | + | statistics.last_contact_reply_at | Date (UNIX timestamp) | + | statistics.last_admin_reply_at | Date (UNIX timestamp) | + | statistics.last_close_at | Date (UNIX timestamp) | + | statistics.last_closed_by_id | String | + | statistics.count_reopens | Integer | + | statistics.count_assignments | Integer | + | statistics.count_conversation_parts | Integer | + | conversation_rating.requested_at | Date (UNIX timestamp) | + | conversation_rating.replied_at | Date (UNIX timestamp) | + | conversation_rating.score | Integer | + | conversation_rating.remark | String | + | conversation_rating.contact_id | String | + | conversation_rating.admin_d | String | + | ai_agent_participated | Boolean | + | ai_agent.resolution_state | String | + | ai_agent.last_answer_type | String | + | ai_agent.rating | Integer | + | ai_agent.rating_remark | String | + | ai_agent.source_type | String | + | ai_agent.source_title | String | + + ### Accepted Operators + + 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 Shortcut for `OR` queries Values most be in Array | + | NIN | All | Not In Shortcut for `OR !` queries Values must be in Array | + | > | Integer Date (UNIX Timestamp) | Greater (or equal) than | + | < | Integer Date (UNIX Timestamp) | Lower (or equal) 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[Conversation, ConversationList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "conversations/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( + ConversationList, + construct_type( + type_=ConversationList, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.conversations + _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) + _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 reply( + self, + conversation_id: str, + *, + request: ReplyConversationRequest, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Conversation]: + """ + You can reply to a conversation with a message from an admin or on behalf of a contact, or with a note for admins. + + Parameters + ---------- + conversation_id : str + The Intercom provisioned identifier for the conversation or the string "last" to reply to the last part of the conversation + + request : ReplyConversationRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Conversation] + User last conversation reply + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/reply", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=ReplyConversationRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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 manage( + self, + conversation_id: str, + *, + request: ConversationsManageRequestBody, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Conversation]: + """ + For managing conversations you can: + - Close a conversation + - Snooze a conversation to reopen on a future date + - Open a conversation which is `snoozed` or `closed` + - Assign a conversation to an admin and/or team. + + Parameters + ---------- + conversation_id : str + The identifier for the conversation as given by Intercom. + + request : ConversationsManageRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Conversation] + Assign a conversation + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/parts", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=ConversationsManageRequestBody, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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_contact_as_admin( + self, + conversation_id: str, + *, + admin_id: typing.Optional[str] = OMIT, + customer: typing.Optional[AttachContactToConversationRequestCustomer] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Conversation]: + """ + You can add participants who are contacts to a conversation, on behalf of either another contact or an admin. + + {% admonition type="warning" name="Contacts without an email" %} + If you add a contact via the email parameter and there is no user/lead found on that workspace with he given email, then we will create a new contact with `role` set to `lead`. + {% /admonition %} + + Parameters + ---------- + conversation_id : str + The identifier for the conversation as given by Intercom. + + admin_id : typing.Optional[str] + The `id` of the admin who is adding the new participant. + + customer : typing.Optional[AttachContactToConversationRequestCustomer] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Conversation] + Attach a contact to a conversation + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/customers", + method="POST", + json={ + "admin_id": admin_id, + "customer": convert_and_respect_annotation_metadata( + object_=customer, annotation=AttachContactToConversationRequestCustomer, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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_contact_as_admin( + self, + conversation_id: str, + contact_id: str, + *, + admin_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Conversation]: + """ + You can add participants who are contacts to a conversation, on behalf of either another contact or an admin. + + {% admonition type="warning" name="Contacts without an email" %} + If you add a contact via the email parameter and there is no user/lead found on that workspace with he given email, then we will create a new contact with `role` set to `lead`. + {% /admonition %} + + Parameters + ---------- + conversation_id : str + The identifier for the conversation as given by Intercom. + + contact_id : str + The identifier for the contact as given by Intercom. + + admin_id : str + The `id` of the admin who is performing the action. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Conversation] + Detach a contact from a group conversation + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/customers/{jsonable_encoder(contact_id)}", + method="DELETE", + json={ + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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 redact_conversation_part( + self, *, request: RedactConversationRequest, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Conversation]: + """ + You can redact a conversation part or the source message of a conversation (as seen in the source object). + + {% admonition type="info" name="Redacting parts and messages" %} + If you are redacting a conversation part, it must have a `body`. If you are redacting a source message, it must have been created by a contact. We will return a `conversation_part_not_redactable` error if these criteria are not met. + {% /admonition %} + + Parameters + ---------- + request : RedactConversationRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Conversation] + Redact a conversation part + """ + _response = await self._client_wrapper.httpx_client.request( + "conversations/redact", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=RedactConversationRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 convert_to_ticket( + self, + conversation_id: int, + *, + ticket_type_id: str, + attributes: typing.Optional[TicketRequestCustomAttributes] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[typing.Optional[Ticket]]: + """ + You can convert a conversation to a ticket. + + Parameters + ---------- + conversation_id : int + The id of the conversation to target + + ticket_type_id : str + The ID of the type of ticket you want to convert the conversation to + + attributes : typing.Optional[TicketRequestCustomAttributes] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[Ticket]] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/convert", + method="POST", + json={ + "ticket_type_id": ticket_type_id, + "attributes": attributes, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Ticket], + construct_type( + type_=typing.Optional[Ticket], # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + 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 run_assignment_rules( + self, conversation_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Conversation]: + """ + {% admonition type="danger" name="Deprecation of Run Assignment Rules" %} + Run assignment rules is now deprecated in version 2.12 and future versions and will be permanently removed on December 31, 2026. After this date, any requests made to this endpoint will fail. + {% /admonition %} + You can let a conversation be automatically assigned following assignment rules. + {% admonition type="warning" name="When using workflows" %} + It is not possible to use this endpoint with Workflows. + {% /admonition %} + + Parameters + ---------- + conversation_id : str + The identifier for the conversation as given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Conversation] + Assign a conversation using assignment rules + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/run_assignment_rules", + method="POST", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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) diff --git a/src/intercom/conversations/types/__init__.py b/src/intercom/conversations/types/__init__.py new file mode 100644 index 00000000..3092a7fe --- /dev/null +++ b/src/intercom/conversations/types/__init__.py @@ -0,0 +1,85 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .attach_contact_to_conversation_request_customer import AttachContactToConversationRequestCustomer + from .attach_contact_to_conversation_request_customer_customer import ( + AttachContactToConversationRequestCustomerCustomer, + ) + from .attach_contact_to_conversation_request_customer_intercom_user_id import ( + AttachContactToConversationRequestCustomerIntercomUserId, + ) + from .attach_contact_to_conversation_request_customer_user_id import ( + AttachContactToConversationRequestCustomerUserId, + ) + from .conversation import Conversation + from .conversation_priority import ConversationPriority + from .conversation_state import ConversationState + from .conversations_manage_request_body import ( + ConversationsManageRequestBody, + ConversationsManageRequestBody_Assignment, + ConversationsManageRequestBody_Close, + ConversationsManageRequestBody_Open, + ConversationsManageRequestBody_Snoozed, + ) + from .create_conversation_request_from import CreateConversationRequestFrom + from .create_conversation_request_from_type import CreateConversationRequestFromType +_dynamic_imports: typing.Dict[str, str] = { + "AttachContactToConversationRequestCustomer": ".attach_contact_to_conversation_request_customer", + "AttachContactToConversationRequestCustomerCustomer": ".attach_contact_to_conversation_request_customer_customer", + "AttachContactToConversationRequestCustomerIntercomUserId": ".attach_contact_to_conversation_request_customer_intercom_user_id", + "AttachContactToConversationRequestCustomerUserId": ".attach_contact_to_conversation_request_customer_user_id", + "Conversation": ".conversation", + "ConversationPriority": ".conversation_priority", + "ConversationState": ".conversation_state", + "ConversationsManageRequestBody": ".conversations_manage_request_body", + "ConversationsManageRequestBody_Assignment": ".conversations_manage_request_body", + "ConversationsManageRequestBody_Close": ".conversations_manage_request_body", + "ConversationsManageRequestBody_Open": ".conversations_manage_request_body", + "ConversationsManageRequestBody_Snoozed": ".conversations_manage_request_body", + "CreateConversationRequestFrom": ".create_conversation_request_from", + "CreateConversationRequestFromType": ".create_conversation_request_from_type", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "AttachContactToConversationRequestCustomer", + "AttachContactToConversationRequestCustomerCustomer", + "AttachContactToConversationRequestCustomerIntercomUserId", + "AttachContactToConversationRequestCustomerUserId", + "Conversation", + "ConversationPriority", + "ConversationState", + "ConversationsManageRequestBody", + "ConversationsManageRequestBody_Assignment", + "ConversationsManageRequestBody_Close", + "ConversationsManageRequestBody_Open", + "ConversationsManageRequestBody_Snoozed", + "CreateConversationRequestFrom", + "CreateConversationRequestFromType", +] diff --git a/src/intercom/conversations/types/attach_contact_to_conversation_request_customer.py b/src/intercom/conversations/types/attach_contact_to_conversation_request_customer.py new file mode 100644 index 00000000..81152a42 --- /dev/null +++ b/src/intercom/conversations/types/attach_contact_to_conversation_request_customer.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .attach_contact_to_conversation_request_customer_customer import AttachContactToConversationRequestCustomerCustomer +from .attach_contact_to_conversation_request_customer_intercom_user_id import ( + AttachContactToConversationRequestCustomerIntercomUserId, +) +from .attach_contact_to_conversation_request_customer_user_id import AttachContactToConversationRequestCustomerUserId + +AttachContactToConversationRequestCustomer = typing.Union[ + AttachContactToConversationRequestCustomerIntercomUserId, + AttachContactToConversationRequestCustomerUserId, + AttachContactToConversationRequestCustomerCustomer, +] diff --git a/src/intercom/conversations/types/attach_contact_to_conversation_request_customer_customer.py b/src/intercom/conversations/types/attach_contact_to_conversation_request_customer_customer.py new file mode 100644 index 00000000..96c13fa3 --- /dev/null +++ b/src/intercom/conversations/types/attach_contact_to_conversation_request_customer_customer.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.customer_request import CustomerRequest + + +class AttachContactToConversationRequestCustomerCustomer(UncheckedBaseModel): + email: str = pydantic.Field() + """ + The email you have defined for the contact who is being added as a participant. + """ + + customer: typing.Optional[CustomerRequest] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/conversations/types/attach_contact_to_conversation_request_customer_intercom_user_id.py b/src/intercom/conversations/types/attach_contact_to_conversation_request_customer_intercom_user_id.py new file mode 100644 index 00000000..acf67196 --- /dev/null +++ b/src/intercom/conversations/types/attach_contact_to_conversation_request_customer_intercom_user_id.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.customer_request import CustomerRequest + + +class AttachContactToConversationRequestCustomerIntercomUserId(UncheckedBaseModel): + intercom_user_id: str = pydantic.Field() + """ + The identifier for the contact as given by Intercom. + """ + + customer: typing.Optional[CustomerRequest] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/conversations/types/attach_contact_to_conversation_request_customer_user_id.py b/src/intercom/conversations/types/attach_contact_to_conversation_request_customer_user_id.py new file mode 100644 index 00000000..b76d5f80 --- /dev/null +++ b/src/intercom/conversations/types/attach_contact_to_conversation_request_customer_user_id.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.customer_request import CustomerRequest + + +class AttachContactToConversationRequestCustomerUserId(UncheckedBaseModel): + user_id: str = pydantic.Field() + """ + The external_id you have defined for the contact who is being added as a participant. + """ + + customer: typing.Optional[CustomerRequest] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/conversations/types/conversation.py b/src/intercom/conversations/types/conversation.py new file mode 100644 index 00000000..84297844 --- /dev/null +++ b/src/intercom/conversations/types/conversation.py @@ -0,0 +1,124 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...ai_agent.types.ai_agent import AiAgent +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.conversation_contacts import ConversationContacts +from ...types.conversation_first_contact_reply import ConversationFirstContactReply +from ...types.conversation_parts import ConversationParts +from ...types.conversation_rating import ConversationRating +from ...types.conversation_source import ConversationSource +from ...types.conversation_statistics import ConversationStatistics +from ...types.conversation_teammates import ConversationTeammates +from ...types.custom_attributes import CustomAttributes +from ...types.linked_object_list import LinkedObjectList +from ...types.sla_applied import SlaApplied +from ...types.tags import Tags +from .conversation_priority import ConversationPriority +from .conversation_state import ConversationState + + +class Conversation(UncheckedBaseModel): + """ + Conversations are how you can communicate with users in Intercom. They are created when a contact replies to an outbound message, or when one admin directly sends a message to a single contact. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + Always conversation. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the conversation. + """ + + title: typing.Optional[str] = pydantic.Field(default=None) + """ + The title given to the conversation. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the conversation was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The last time the conversation was updated. + """ + + waiting_since: typing.Optional[int] = pydantic.Field(default=None) + """ + The last time a Contact responded to an Admin. In other words, the time a customer started waiting for a response. Set to null if last reply is from an Admin. + """ + + snoozed_until: typing.Optional[int] = pydantic.Field(default=None) + """ + If set this is the time in the future when this conversation will be marked as open. i.e. it will be in a snoozed state until this time. i.e. it will be in a snoozed state until this time. + """ + + open: typing.Optional[bool] = pydantic.Field(default=None) + """ + Indicates whether a conversation is open (true) or closed (false). + """ + + state: typing.Optional[ConversationState] = pydantic.Field(default=None) + """ + Can be set to "open", "closed" or "snoozed". + """ + + read: typing.Optional[bool] = pydantic.Field(default=None) + """ + Indicates whether a conversation has been read. + """ + + priority: typing.Optional[ConversationPriority] = pydantic.Field(default=None) + """ + If marked as priority, it will return priority or else not_priority. + """ + + admin_assignee_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of the admin assigned to the conversation. If it's not assigned to an admin it will return null. + """ + + team_assignee_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the team assigned to the conversation. If it's not assigned to a team it will return null. + """ + + company_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the company that the conversation is associated with. The unique identifier for the company which is given by Intercom. + """ + + tags: typing.Optional[Tags] = None + conversation_rating: typing.Optional[ConversationRating] = None + source: typing.Optional[ConversationSource] = None + contacts: typing.Optional[ConversationContacts] = None + teammates: typing.Optional[ConversationTeammates] = None + custom_attributes: typing.Optional[CustomAttributes] = None + first_contact_reply: typing.Optional[ConversationFirstContactReply] = None + sla_applied: typing.Optional[SlaApplied] = None + statistics: typing.Optional[ConversationStatistics] = None + conversation_parts: typing.Optional[ConversationParts] = None + linked_objects: typing.Optional[LinkedObjectList] = None + ai_agent_participated: typing.Optional[bool] = pydantic.Field(default=None) + """ + Indicates whether the AI Agent participated in the conversation. + """ + + ai_agent: typing.Optional[AiAgent] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/conversations/types/conversation_priority.py b/src/intercom/conversations/types/conversation_priority.py new file mode 100644 index 00000000..c70400ba --- /dev/null +++ b/src/intercom/conversations/types/conversation_priority.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ConversationPriority = typing.Union[typing.Literal["priority", "not_priority"], typing.Any] diff --git a/src/intercom/conversations/types/conversation_state.py b/src/intercom/conversations/types/conversation_state.py new file mode 100644 index 00000000..2353c122 --- /dev/null +++ b/src/intercom/conversations/types/conversation_state.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ConversationState = typing.Union[typing.Literal["open", "closed", "snoozed"], typing.Any] diff --git a/src/intercom/conversations/types/conversations_manage_request_body.py b/src/intercom/conversations/types/conversations_manage_request_body.py new file mode 100644 index 00000000..19c0b9f1 --- /dev/null +++ b/src/intercom/conversations/types/conversations_manage_request_body.py @@ -0,0 +1,84 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel, UnionMetadata +from ...types.assign_conversation_request_type import AssignConversationRequestType + + +class ConversationsManageRequestBody_Close(UncheckedBaseModel): + message_type: typing.Literal["close"] = "close" + type: typing.Literal["admin"] = "admin" + admin_id: str + body: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConversationsManageRequestBody_Snoozed(UncheckedBaseModel): + message_type: typing.Literal["snoozed"] = "snoozed" + admin_id: str + snoozed_until: int + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConversationsManageRequestBody_Open(UncheckedBaseModel): + message_type: typing.Literal["open"] = "open" + admin_id: str + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ConversationsManageRequestBody_Assignment(UncheckedBaseModel): + message_type: typing.Literal["assignment"] = "assignment" + type: AssignConversationRequestType + admin_id: str + assignee_id: str + body: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +ConversationsManageRequestBody = typing_extensions.Annotated[ + typing.Union[ + ConversationsManageRequestBody_Close, + ConversationsManageRequestBody_Snoozed, + ConversationsManageRequestBody_Open, + ConversationsManageRequestBody_Assignment, + ], + UnionMetadata(discriminant="message_type"), +] diff --git a/src/intercom/conversations/types/create_conversation_request_from.py b/src/intercom/conversations/types/create_conversation_request_from.py new file mode 100644 index 00000000..a3910f29 --- /dev/null +++ b/src/intercom/conversations/types/create_conversation_request_from.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .create_conversation_request_from_type import CreateConversationRequestFromType + + +class CreateConversationRequestFrom(UncheckedBaseModel): + type: CreateConversationRequestFromType = pydantic.Field() + """ + The role associated to the contact - user or lead. + """ + + id: str = pydantic.Field() + """ + The identifier for the contact which is given by Intercom. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/conversations/types/create_conversation_request_from_type.py b/src/intercom/conversations/types/create_conversation_request_from_type.py new file mode 100644 index 00000000..bd885295 --- /dev/null +++ b/src/intercom/conversations/types/create_conversation_request_from_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CreateConversationRequestFromType = typing.Union[typing.Literal["lead", "user", "contact"], typing.Any] diff --git a/src/intercom/core/__init__.py b/src/intercom/core/__init__.py new file mode 100644 index 00000000..e4c2e91c --- /dev/null +++ b/src/intercom/core/__init__.py @@ -0,0 +1,122 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .api_error import ApiError + from .client_wrapper import AsyncClientWrapper, BaseClientWrapper, SyncClientWrapper + from .custom_pagination import AsyncCustomPager, SyncCustomPager + from .datetime_utils import serialize_datetime + from .file import File, convert_file_dict_to_httpx_tuples, with_content_type + from .http_client import AsyncHttpClient, HttpClient + from .http_response import AsyncHttpResponse, HttpResponse + from .jsonable_encoder import jsonable_encoder + from .pagination import AsyncPager, SyncPager + from .pydantic_utilities import ( + IS_PYDANTIC_V2, + UniversalBaseModel, + UniversalRootModel, + parse_obj_as, + universal_field_validator, + universal_root_validator, + update_forward_refs, + ) + from .query_encoder import encode_query + from .remove_none_from_dict import remove_none_from_dict + from .request_options import RequestOptions + from .serialization import FieldMetadata, convert_and_respect_annotation_metadata + from .unchecked_base_model import UncheckedBaseModel, UnionMetadata, construct_type +_dynamic_imports: typing.Dict[str, str] = { + "ApiError": ".api_error", + "AsyncClientWrapper": ".client_wrapper", + "AsyncCustomPager": ".custom_pagination", + "AsyncHttpClient": ".http_client", + "AsyncHttpResponse": ".http_response", + "AsyncPager": ".pagination", + "BaseClientWrapper": ".client_wrapper", + "FieldMetadata": ".serialization", + "File": ".file", + "HttpClient": ".http_client", + "HttpResponse": ".http_response", + "IS_PYDANTIC_V2": ".pydantic_utilities", + "RequestOptions": ".request_options", + "SyncClientWrapper": ".client_wrapper", + "SyncCustomPager": ".custom_pagination", + "SyncPager": ".pagination", + "UncheckedBaseModel": ".unchecked_base_model", + "UnionMetadata": ".unchecked_base_model", + "UniversalBaseModel": ".pydantic_utilities", + "UniversalRootModel": ".pydantic_utilities", + "construct_type": ".unchecked_base_model", + "convert_and_respect_annotation_metadata": ".serialization", + "convert_file_dict_to_httpx_tuples": ".file", + "encode_query": ".query_encoder", + "jsonable_encoder": ".jsonable_encoder", + "parse_obj_as": ".pydantic_utilities", + "remove_none_from_dict": ".remove_none_from_dict", + "serialize_datetime": ".datetime_utils", + "universal_field_validator": ".pydantic_utilities", + "universal_root_validator": ".pydantic_utilities", + "update_forward_refs": ".pydantic_utilities", + "with_content_type": ".file", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "ApiError", + "AsyncClientWrapper", + "AsyncCustomPager", + "AsyncHttpClient", + "AsyncHttpResponse", + "AsyncPager", + "BaseClientWrapper", + "FieldMetadata", + "File", + "HttpClient", + "HttpResponse", + "IS_PYDANTIC_V2", + "RequestOptions", + "SyncClientWrapper", + "SyncCustomPager", + "SyncPager", + "UncheckedBaseModel", + "UnionMetadata", + "UniversalBaseModel", + "UniversalRootModel", + "construct_type", + "convert_and_respect_annotation_metadata", + "convert_file_dict_to_httpx_tuples", + "encode_query", + "jsonable_encoder", + "parse_obj_as", + "remove_none_from_dict", + "serialize_datetime", + "universal_field_validator", + "universal_root_validator", + "update_forward_refs", + "with_content_type", +] diff --git a/src/intercom/core/api_error.py b/src/intercom/core/api_error.py new file mode 100644 index 00000000..6f850a60 --- /dev/null +++ b/src/intercom/core/api_error.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict, Optional + + +class ApiError(Exception): + headers: Optional[Dict[str, str]] + status_code: Optional[int] + body: Any + + def __init__( + self, + *, + headers: Optional[Dict[str, str]] = None, + status_code: Optional[int] = None, + body: Any = None, + ) -> None: + self.headers = headers + self.status_code = status_code + self.body = body + + def __str__(self) -> str: + return f"headers: {self.headers}, status_code: {self.status_code}, body: {self.body}" diff --git a/src/intercom/core/client_wrapper.py b/src/intercom/core/client_wrapper.py new file mode 100644 index 00000000..c0ad61f8 --- /dev/null +++ b/src/intercom/core/client_wrapper.py @@ -0,0 +1,85 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import httpx +from .http_client import AsyncHttpClient, HttpClient + + +class BaseClientWrapper: + def __init__( + self, + *, + token: typing.Union[str, typing.Callable[[], str]], + headers: typing.Optional[typing.Dict[str, str]] = None, + base_url: str, + timeout: typing.Optional[float] = None, + ): + self._token = token + self._headers = headers + self._base_url = base_url + self._timeout = timeout + + def get_headers(self) -> typing.Dict[str, str]: + headers: typing.Dict[str, str] = { + "User-Agent": "python-intercom/5.0.0", + "X-Fern-Language": "Python", + "X-Fern-SDK-Name": "python-intercom", + "X-Fern-SDK-Version": "5.0.0", + **(self.get_custom_headers() or {}), + } + headers["Authorization"] = f"Bearer {self._get_token()}" + return headers + + def _get_token(self) -> str: + if isinstance(self._token, str): + return self._token + else: + return self._token() + + def get_custom_headers(self) -> typing.Optional[typing.Dict[str, str]]: + return self._headers + + def get_base_url(self) -> str: + return self._base_url + + def get_timeout(self) -> typing.Optional[float]: + return self._timeout + + +class SyncClientWrapper(BaseClientWrapper): + def __init__( + self, + *, + token: typing.Union[str, typing.Callable[[], str]], + headers: typing.Optional[typing.Dict[str, str]] = None, + base_url: str, + timeout: typing.Optional[float] = None, + httpx_client: httpx.Client, + ): + super().__init__(token=token, headers=headers, base_url=base_url, timeout=timeout) + self.httpx_client = HttpClient( + httpx_client=httpx_client, + base_headers=self.get_headers, + base_timeout=self.get_timeout, + base_url=self.get_base_url, + ) + + +class AsyncClientWrapper(BaseClientWrapper): + def __init__( + self, + *, + token: typing.Union[str, typing.Callable[[], str]], + headers: typing.Optional[typing.Dict[str, str]] = None, + base_url: str, + timeout: typing.Optional[float] = None, + httpx_client: httpx.AsyncClient, + ): + super().__init__(token=token, headers=headers, base_url=base_url, timeout=timeout) + self.httpx_client = AsyncHttpClient( + httpx_client=httpx_client, + base_headers=self.get_headers, + base_timeout=self.get_timeout, + base_url=self.get_base_url, + ) diff --git a/src/intercom/core/custom_pagination.py b/src/intercom/core/custom_pagination.py new file mode 100644 index 00000000..5de2c7a8 --- /dev/null +++ b/src/intercom/core/custom_pagination.py @@ -0,0 +1,152 @@ +# This file was auto-generated by Fern from our API Definition. + +""" +Custom Pagination Support + +This file is designed to be modified by SDK users to implement their own +pagination logic. The generator will import SyncCustomPager and AsyncCustomPager +from this module when custom pagination is used. + +Users should: +1. Implement their custom pager (e.g., PayrocPager, MyCustomPager, etc.) +2. Create adapter classes (SyncCustomPager/AsyncCustomPager) that bridge + between the generated SDK code and their custom pager implementation +""" + +from __future__ import annotations + +from typing import Any, AsyncIterator, Generic, Iterator, TypeVar + +# Import the base utilities you'll need +# Adjust these imports based on your actual structure +try: + from .client_wrapper import AsyncClientWrapper, SyncClientWrapper +except ImportError: + # Fallback for type hints + AsyncClientWrapper = Any # type: ignore + SyncClientWrapper = Any # type: ignore + +TItem = TypeVar("TItem") +TResponse = TypeVar("TResponse") + + +class SyncCustomPager(Generic[TItem, TResponse]): + """ + Adapter for custom synchronous pagination. + + The generator will call this with: + SyncCustomPager(initial_response=response, client_wrapper=client_wrapper) + + Implement this class to extract pagination metadata from your response + and delegate to your custom pager implementation. + + Example implementation: + + class SyncCustomPager(Generic[TItem, TResponse]): + def __init__( + self, + *, + initial_response: TResponse, + client_wrapper: SyncClientWrapper, + ): + # Extract data and pagination metadata from response + data = initial_response.data # Adjust based on your response structure + links = initial_response.links + + # Initialize your custom pager + self._pager = MyCustomPager( + current_page=Page(data), + httpx_client=client_wrapper.httpx_client, + get_headers=client_wrapper.get_headers, + # ... other parameters + ) + + def __iter__(self): + return iter(self._pager) + + # Delegate other methods to your pager... + """ + + def __init__( + self, + *, + initial_response: TResponse, + client_wrapper: SyncClientWrapper, + ): + """ + Initialize the custom pager. + + Args: + initial_response: The parsed API response from the first request + client_wrapper: The client wrapper providing HTTP client and utilities + """ + raise NotImplementedError( + "SyncCustomPager must be implemented. " + "Please implement this class in core/custom_pagination.py to define your pagination logic. " + "See the class docstring for examples." + ) + + def __iter__(self) -> Iterator[TItem]: + """Iterate through all items across all pages.""" + raise NotImplementedError("Must implement __iter__ method") + + +class AsyncCustomPager(Generic[TItem, TResponse]): + """ + Adapter for custom asynchronous pagination. + + The generator will call this with: + AsyncCustomPager(initial_response=response, client_wrapper=client_wrapper) + + Implement this class to extract pagination metadata from your response + and delegate to your custom async pager implementation. + + Example implementation: + + class AsyncCustomPager(Generic[TItem, TResponse]): + def __init__( + self, + *, + initial_response: TResponse, + client_wrapper: AsyncClientWrapper, + ): + # Extract data and pagination metadata from response + data = initial_response.data # Adjust based on your response structure + links = initial_response.links + + # Initialize your custom async pager + self._pager = MyAsyncCustomPager( + current_page=Page(data), + httpx_client=client_wrapper.httpx_client, + get_headers=client_wrapper.get_headers, + # ... other parameters + ) + + async def __aiter__(self): + return self._pager.__aiter__() + + # Delegate other methods to your pager... + """ + + def __init__( + self, + *, + initial_response: TResponse, + client_wrapper: AsyncClientWrapper, + ): + """ + Initialize the custom async pager. + + Args: + initial_response: The parsed API response from the first request + client_wrapper: The client wrapper providing HTTP client and utilities + """ + raise NotImplementedError( + "AsyncCustomPager must be implemented. " + "Please implement this class in core/custom_pagination.py to define your pagination logic. " + "See the class docstring for examples." + ) + + async def __aiter__(self) -> AsyncIterator[TItem]: + """Asynchronously iterate through all items across all pages.""" + raise NotImplementedError("Must implement __aiter__ method") diff --git a/src/intercom/core/datetime_utils.py b/src/intercom/core/datetime_utils.py new file mode 100644 index 00000000..7c9864a9 --- /dev/null +++ b/src/intercom/core/datetime_utils.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt + + +def serialize_datetime(v: dt.datetime) -> str: + """ + Serialize a datetime including timezone info. + + Uses the timezone info provided if present, otherwise uses the current runtime's timezone info. + + UTC datetimes end in "Z" while all other timezones are represented as offset from UTC, e.g. +05:00. + """ + + def _serialize_zoned_datetime(v: dt.datetime) -> str: + if v.tzinfo is not None and v.tzinfo.tzname(None) == dt.timezone.utc.tzname(None): + # UTC is a special case where we use "Z" at the end instead of "+00:00" + return v.isoformat().replace("+00:00", "Z") + else: + # Delegate to the typical +/- offset format + return v.isoformat() + + if v.tzinfo is not None: + return _serialize_zoned_datetime(v) + else: + local_tz = dt.datetime.now().astimezone().tzinfo + localized_dt = v.replace(tzinfo=local_tz) + return _serialize_zoned_datetime(localized_dt) diff --git a/src/intercom/core/file.py b/src/intercom/core/file.py new file mode 100644 index 00000000..44b0d27c --- /dev/null +++ b/src/intercom/core/file.py @@ -0,0 +1,67 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import IO, Dict, List, Mapping, Optional, Tuple, Union, cast + +# File typing inspired by the flexibility of types within the httpx library +# https://github.com/encode/httpx/blob/master/httpx/_types.py +FileContent = Union[IO[bytes], bytes, str] +File = Union[ + # file (or bytes) + FileContent, + # (filename, file (or bytes)) + Tuple[Optional[str], FileContent], + # (filename, file (or bytes), content_type) + Tuple[Optional[str], FileContent, Optional[str]], + # (filename, file (or bytes), content_type, headers) + Tuple[ + Optional[str], + FileContent, + Optional[str], + Mapping[str, str], + ], +] + + +def convert_file_dict_to_httpx_tuples( + d: Dict[str, Union[File, List[File]]], +) -> List[Tuple[str, File]]: + """ + The format we use is a list of tuples, where the first element is the + name of the file and the second is the file object. Typically HTTPX wants + a dict, but to be able to send lists of files, you have to use the list + approach (which also works for non-lists) + https://github.com/encode/httpx/pull/1032 + """ + + httpx_tuples = [] + for key, file_like in d.items(): + if isinstance(file_like, list): + for file_like_item in file_like: + httpx_tuples.append((key, file_like_item)) + else: + httpx_tuples.append((key, file_like)) + return httpx_tuples + + +def with_content_type(*, file: File, default_content_type: str) -> File: + """ + This function resolves to the file's content type, if provided, and defaults + to the default_content_type value if not. + """ + if isinstance(file, tuple): + if len(file) == 2: + filename, content = cast(Tuple[Optional[str], FileContent], file) # type: ignore + return (filename, content, default_content_type) + elif len(file) == 3: + filename, content, file_content_type = cast(Tuple[Optional[str], FileContent, Optional[str]], file) # type: ignore + out_content_type = file_content_type or default_content_type + return (filename, content, out_content_type) + elif len(file) == 4: + filename, content, file_content_type, headers = cast( # type: ignore + Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], file + ) + out_content_type = file_content_type or default_content_type + return (filename, content, out_content_type, headers) + else: + raise ValueError(f"Unexpected tuple length: {len(file)}") + return (None, file, default_content_type) diff --git a/src/intercom/core/force_multipart.py b/src/intercom/core/force_multipart.py new file mode 100644 index 00000000..5440913f --- /dev/null +++ b/src/intercom/core/force_multipart.py @@ -0,0 +1,18 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict + + +class ForceMultipartDict(Dict[str, Any]): + """ + A dictionary subclass that always evaluates to True in boolean contexts. + + This is used to force multipart/form-data encoding in HTTP requests even when + the dictionary is empty, which would normally evaluate to False. + """ + + def __bool__(self) -> bool: + return True + + +FORCE_MULTIPART = ForceMultipartDict() diff --git a/src/intercom/core/http_client.py b/src/intercom/core/http_client.py new file mode 100644 index 00000000..901b55f8 --- /dev/null +++ b/src/intercom/core/http_client.py @@ -0,0 +1,600 @@ +# This file was auto-generated by Fern from our API Definition. + +import asyncio +import email.utils +import re +import time +import typing +import urllib.parse +from contextlib import asynccontextmanager, contextmanager +from random import random + +import httpx +from .file import File, convert_file_dict_to_httpx_tuples +from .force_multipart import FORCE_MULTIPART +from .jsonable_encoder import jsonable_encoder +from .query_encoder import encode_query +from .remove_none_from_dict import remove_none_from_dict as remove_none_from_dict +from .request_options import RequestOptions +from httpx._types import RequestFiles + +INITIAL_RETRY_DELAY_SECONDS = 1.0 +MAX_RETRY_DELAY_SECONDS = 60.0 +JITTER_FACTOR = 0.2 # 20% random jitter + + +def _parse_retry_after(response_headers: httpx.Headers) -> typing.Optional[float]: + """ + This function parses the `Retry-After` header in a HTTP response and returns the number of seconds to wait. + + Inspired by the urllib3 retry implementation. + """ + retry_after_ms = response_headers.get("retry-after-ms") + if retry_after_ms is not None: + try: + return int(retry_after_ms) / 1000 if retry_after_ms > 0 else 0 + except Exception: + pass + + retry_after = response_headers.get("retry-after") + if retry_after is None: + return None + + # Attempt to parse the header as an int. + if re.match(r"^\s*[0-9]+\s*$", retry_after): + seconds = float(retry_after) + # Fallback to parsing it as a date. + else: + retry_date_tuple = email.utils.parsedate_tz(retry_after) + if retry_date_tuple is None: + return None + if retry_date_tuple[9] is None: # Python 2 + # Assume UTC if no timezone was specified + # On Python2.7, parsedate_tz returns None for a timezone offset + # instead of 0 if no timezone is given, where mktime_tz treats + # a None timezone offset as local time. + retry_date_tuple = retry_date_tuple[:9] + (0,) + retry_date_tuple[10:] + + retry_date = email.utils.mktime_tz(retry_date_tuple) + seconds = retry_date - time.time() + + if seconds < 0: + seconds = 0 + + return seconds + + +def _add_positive_jitter(delay: float) -> float: + """Add positive jitter (0-20%) to prevent thundering herd.""" + jitter_multiplier = 1 + random() * JITTER_FACTOR + return delay * jitter_multiplier + + +def _add_symmetric_jitter(delay: float) -> float: + """Add symmetric jitter (±10%) for exponential backoff.""" + jitter_multiplier = 1 + (random() - 0.5) * JITTER_FACTOR + return delay * jitter_multiplier + + +def _parse_x_ratelimit_reset(response_headers: httpx.Headers) -> typing.Optional[float]: + """ + Parse the X-RateLimit-Reset header (Unix timestamp in seconds). + Returns seconds to wait, or None if header is missing/invalid. + """ + reset_time_str = response_headers.get("x-ratelimit-reset") + if reset_time_str is None: + return None + + try: + reset_time = int(reset_time_str) + delay = reset_time - time.time() + if delay > 0: + return delay + except (ValueError, TypeError): + pass + + return None + + +def _retry_timeout(response: httpx.Response, retries: int) -> float: + """ + Determine the amount of time to wait before retrying a request. + This function begins by trying to parse a retry-after header from the response, and then proceeds to use exponential backoff + with a jitter to determine the number of seconds to wait. + """ + + # 1. Check Retry-After header first + retry_after = _parse_retry_after(response.headers) + if retry_after is not None and retry_after > 0: + return min(retry_after, MAX_RETRY_DELAY_SECONDS) + + # 2. Check X-RateLimit-Reset header (with positive jitter) + ratelimit_reset = _parse_x_ratelimit_reset(response.headers) + if ratelimit_reset is not None: + return _add_positive_jitter(min(ratelimit_reset, MAX_RETRY_DELAY_SECONDS)) + + # 3. Fall back to exponential backoff (with symmetric jitter) + backoff = min(INITIAL_RETRY_DELAY_SECONDS * pow(2.0, retries), MAX_RETRY_DELAY_SECONDS) + return _add_symmetric_jitter(backoff) + + +def _should_retry(response: httpx.Response) -> bool: + retryable_400s = [429, 408, 409] + return response.status_code >= 500 or response.status_code in retryable_400s + + +def _maybe_filter_none_from_multipart_data( + data: typing.Optional[typing.Any], + request_files: typing.Optional[RequestFiles], + force_multipart: typing.Optional[bool], +) -> typing.Optional[typing.Any]: + """ + Filter None values from data body for multipart/form requests. + This prevents httpx from converting None to empty strings in multipart encoding. + Only applies when files are present or force_multipart is True. + """ + if data is not None and isinstance(data, typing.Mapping) and (request_files or force_multipart): + return remove_none_from_dict(data) + return data + + +def remove_omit_from_dict( + original: typing.Dict[str, typing.Optional[typing.Any]], + omit: typing.Optional[typing.Any], +) -> typing.Dict[str, typing.Any]: + if omit is None: + return original + new: typing.Dict[str, typing.Any] = {} + for key, value in original.items(): + if value is not omit: + new[key] = value + return new + + +def maybe_filter_request_body( + data: typing.Optional[typing.Any], + request_options: typing.Optional[RequestOptions], + omit: typing.Optional[typing.Any], +) -> typing.Optional[typing.Any]: + if data is None: + return ( + jsonable_encoder(request_options.get("additional_body_parameters", {})) or {} + if request_options is not None + else None + ) + elif not isinstance(data, typing.Mapping): + data_content = jsonable_encoder(data) + else: + data_content = { + **(jsonable_encoder(remove_omit_from_dict(data, omit))), # type: ignore + **( + jsonable_encoder(request_options.get("additional_body_parameters", {})) or {} + if request_options is not None + else {} + ), + } + return data_content + + +# Abstracted out for testing purposes +def get_request_body( + *, + json: typing.Optional[typing.Any], + data: typing.Optional[typing.Any], + request_options: typing.Optional[RequestOptions], + omit: typing.Optional[typing.Any], +) -> typing.Tuple[typing.Optional[typing.Any], typing.Optional[typing.Any]]: + json_body = None + data_body = None + if data is not None: + data_body = maybe_filter_request_body(data, request_options, omit) + else: + # If both data and json are None, we send json data in the event extra properties are specified + json_body = maybe_filter_request_body(json, request_options, omit) + + # If you have an empty JSON body, you should just send None + return (json_body if json_body != {} else None), data_body if data_body != {} else None + + +class HttpClient: + def __init__( + self, + *, + httpx_client: httpx.Client, + base_timeout: typing.Callable[[], typing.Optional[float]], + base_headers: typing.Callable[[], typing.Dict[str, str]], + base_url: typing.Optional[typing.Callable[[], str]] = None, + ): + self.base_url = base_url + self.base_timeout = base_timeout + self.base_headers = base_headers + self.httpx_client = httpx_client + + def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str: + base_url = maybe_base_url + if self.base_url is not None and base_url is None: + base_url = self.base_url() + + if base_url is None: + raise ValueError("A base_url is required to make this request, please provide one and try again.") + return base_url + + def request( + self, + path: typing.Optional[str] = None, + *, + method: str, + base_url: typing.Optional[str] = None, + params: typing.Optional[typing.Dict[str, typing.Any]] = None, + json: typing.Optional[typing.Any] = None, + data: typing.Optional[typing.Any] = None, + content: typing.Optional[typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]] = None, + files: typing.Optional[ + typing.Union[ + typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]], + typing.List[typing.Tuple[str, File]], + ] + ] = None, + headers: typing.Optional[typing.Dict[str, typing.Any]] = None, + request_options: typing.Optional[RequestOptions] = None, + retries: int = 2, + omit: typing.Optional[typing.Any] = None, + force_multipart: typing.Optional[bool] = None, + ) -> httpx.Response: + base_url = self.get_base_url(base_url) + timeout = ( + request_options.get("timeout_in_seconds") + if request_options is not None and request_options.get("timeout_in_seconds") is not None + else self.base_timeout() + ) + + json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + + request_files: typing.Optional[RequestFiles] = ( + convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit)) + if (files is not None and files is not omit and isinstance(files, dict)) + else None + ) + + if (request_files is None or len(request_files) == 0) and force_multipart: + request_files = FORCE_MULTIPART + + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + response = self.httpx_client.request( + method=method, + url=urllib.parse.urljoin(f"{base_url}/", path), + headers=jsonable_encoder( + remove_none_from_dict( + { + **self.base_headers(), + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), + } + ) + ), + params=encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ), + json=json_body, + data=data_body, + content=content, + files=request_files, + timeout=timeout, + ) + + max_retries: int = request_options.get("max_retries", 0) if request_options is not None else 0 + if _should_retry(response=response): + if max_retries > retries: + time.sleep(_retry_timeout(response=response, retries=retries)) + return self.request( + path=path, + method=method, + base_url=base_url, + params=params, + json=json, + content=content, + files=files, + headers=headers, + request_options=request_options, + retries=retries + 1, + omit=omit, + ) + + return response + + @contextmanager + def stream( + self, + path: typing.Optional[str] = None, + *, + method: str, + base_url: typing.Optional[str] = None, + params: typing.Optional[typing.Dict[str, typing.Any]] = None, + json: typing.Optional[typing.Any] = None, + data: typing.Optional[typing.Any] = None, + content: typing.Optional[typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]] = None, + files: typing.Optional[ + typing.Union[ + typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]], + typing.List[typing.Tuple[str, File]], + ] + ] = None, + headers: typing.Optional[typing.Dict[str, typing.Any]] = None, + request_options: typing.Optional[RequestOptions] = None, + retries: int = 2, + omit: typing.Optional[typing.Any] = None, + force_multipart: typing.Optional[bool] = None, + ) -> typing.Iterator[httpx.Response]: + base_url = self.get_base_url(base_url) + timeout = ( + request_options.get("timeout_in_seconds") + if request_options is not None and request_options.get("timeout_in_seconds") is not None + else self.base_timeout() + ) + + request_files: typing.Optional[RequestFiles] = ( + convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit)) + if (files is not None and files is not omit and isinstance(files, dict)) + else None + ) + + if (request_files is None or len(request_files) == 0) and force_multipart: + request_files = FORCE_MULTIPART + + json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + with self.httpx_client.stream( + method=method, + url=urllib.parse.urljoin(f"{base_url}/", path), + headers=jsonable_encoder( + remove_none_from_dict( + { + **self.base_headers(), + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) if request_options is not None else {}), + } + ) + ), + params=encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ), + json=json_body, + data=data_body, + content=content, + files=request_files, + timeout=timeout, + ) as stream: + yield stream + + +class AsyncHttpClient: + def __init__( + self, + *, + httpx_client: httpx.AsyncClient, + base_timeout: typing.Callable[[], typing.Optional[float]], + base_headers: typing.Callable[[], typing.Dict[str, str]], + base_url: typing.Optional[typing.Callable[[], str]] = None, + ): + self.base_url = base_url + self.base_timeout = base_timeout + self.base_headers = base_headers + self.httpx_client = httpx_client + + def get_base_url(self, maybe_base_url: typing.Optional[str]) -> str: + base_url = maybe_base_url + if self.base_url is not None and base_url is None: + base_url = self.base_url() + + if base_url is None: + raise ValueError("A base_url is required to make this request, please provide one and try again.") + return base_url + + async def request( + self, + path: typing.Optional[str] = None, + *, + method: str, + base_url: typing.Optional[str] = None, + params: typing.Optional[typing.Dict[str, typing.Any]] = None, + json: typing.Optional[typing.Any] = None, + data: typing.Optional[typing.Any] = None, + content: typing.Optional[typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]] = None, + files: typing.Optional[ + typing.Union[ + typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]], + typing.List[typing.Tuple[str, File]], + ] + ] = None, + headers: typing.Optional[typing.Dict[str, typing.Any]] = None, + request_options: typing.Optional[RequestOptions] = None, + retries: int = 2, + omit: typing.Optional[typing.Any] = None, + force_multipart: typing.Optional[bool] = None, + ) -> httpx.Response: + base_url = self.get_base_url(base_url) + timeout = ( + request_options.get("timeout_in_seconds") + if request_options is not None and request_options.get("timeout_in_seconds") is not None + else self.base_timeout() + ) + + request_files: typing.Optional[RequestFiles] = ( + convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit)) + if (files is not None and files is not omit and isinstance(files, dict)) + else None + ) + + if (request_files is None or len(request_files) == 0) and force_multipart: + request_files = FORCE_MULTIPART + + json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + # Add the input to each of these and do None-safety checks + response = await self.httpx_client.request( + method=method, + url=urllib.parse.urljoin(f"{base_url}/", path), + headers=jsonable_encoder( + remove_none_from_dict( + { + **self.base_headers(), + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) or {} if request_options is not None else {}), + } + ) + ), + params=encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) or {} + if request_options is not None + else {} + ), + }, + omit, + ) + ) + ) + ), + json=json_body, + data=data_body, + content=content, + files=request_files, + timeout=timeout, + ) + + max_retries: int = request_options.get("max_retries", 0) if request_options is not None else 0 + if _should_retry(response=response): + if max_retries > retries: + await asyncio.sleep(_retry_timeout(response=response, retries=retries)) + return await self.request( + path=path, + method=method, + base_url=base_url, + params=params, + json=json, + content=content, + files=files, + headers=headers, + request_options=request_options, + retries=retries + 1, + omit=omit, + ) + return response + + @asynccontextmanager + async def stream( + self, + path: typing.Optional[str] = None, + *, + method: str, + base_url: typing.Optional[str] = None, + params: typing.Optional[typing.Dict[str, typing.Any]] = None, + json: typing.Optional[typing.Any] = None, + data: typing.Optional[typing.Any] = None, + content: typing.Optional[typing.Union[bytes, typing.Iterator[bytes], typing.AsyncIterator[bytes]]] = None, + files: typing.Optional[ + typing.Union[ + typing.Dict[str, typing.Optional[typing.Union[File, typing.List[File]]]], + typing.List[typing.Tuple[str, File]], + ] + ] = None, + headers: typing.Optional[typing.Dict[str, typing.Any]] = None, + request_options: typing.Optional[RequestOptions] = None, + retries: int = 2, + omit: typing.Optional[typing.Any] = None, + force_multipart: typing.Optional[bool] = None, + ) -> typing.AsyncIterator[httpx.Response]: + base_url = self.get_base_url(base_url) + timeout = ( + request_options.get("timeout_in_seconds") + if request_options is not None and request_options.get("timeout_in_seconds") is not None + else self.base_timeout() + ) + + request_files: typing.Optional[RequestFiles] = ( + convert_file_dict_to_httpx_tuples(remove_omit_from_dict(remove_none_from_dict(files), omit)) + if (files is not None and files is not omit and isinstance(files, dict)) + else None + ) + + if (request_files is None or len(request_files) == 0) and force_multipart: + request_files = FORCE_MULTIPART + + json_body, data_body = get_request_body(json=json, data=data, request_options=request_options, omit=omit) + + data_body = _maybe_filter_none_from_multipart_data(data_body, request_files, force_multipart) + + async with self.httpx_client.stream( + method=method, + url=urllib.parse.urljoin(f"{base_url}/", path), + headers=jsonable_encoder( + remove_none_from_dict( + { + **self.base_headers(), + **(headers if headers is not None else {}), + **(request_options.get("additional_headers", {}) if request_options is not None else {}), + } + ) + ), + params=encode_query( + jsonable_encoder( + remove_none_from_dict( + remove_omit_from_dict( + { + **(params if params is not None else {}), + **( + request_options.get("additional_query_parameters", {}) + if request_options is not None + else {} + ), + }, + omit=omit, + ) + ) + ) + ), + json=json_body, + data=data_body, + content=content, + files=request_files, + timeout=timeout, + ) as stream: + yield stream diff --git a/src/intercom/core/http_response.py b/src/intercom/core/http_response.py new file mode 100644 index 00000000..2479747e --- /dev/null +++ b/src/intercom/core/http_response.py @@ -0,0 +1,55 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Dict, Generic, TypeVar + +import httpx + +# Generic to represent the underlying type of the data wrapped by the HTTP response. +T = TypeVar("T") + + +class BaseHttpResponse: + """Minimalist HTTP response wrapper that exposes response headers.""" + + _response: httpx.Response + + def __init__(self, response: httpx.Response): + self._response = response + + @property + def headers(self) -> Dict[str, str]: + return dict(self._response.headers) + + +class HttpResponse(Generic[T], BaseHttpResponse): + """HTTP response wrapper that exposes response headers and data.""" + + _data: T + + def __init__(self, response: httpx.Response, data: T): + super().__init__(response) + self._data = data + + @property + def data(self) -> T: + return self._data + + def close(self) -> None: + self._response.close() + + +class AsyncHttpResponse(Generic[T], BaseHttpResponse): + """HTTP response wrapper that exposes response headers and data.""" + + _data: T + + def __init__(self, response: httpx.Response, data: T): + super().__init__(response) + self._data = data + + @property + def data(self) -> T: + return self._data + + async def close(self) -> None: + await self._response.aclose() diff --git a/src/intercom/core/http_sse/__init__.py b/src/intercom/core/http_sse/__init__.py new file mode 100644 index 00000000..730e5a33 --- /dev/null +++ b/src/intercom/core/http_sse/__init__.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from ._api import EventSource, aconnect_sse, connect_sse + from ._exceptions import SSEError + from ._models import ServerSentEvent +_dynamic_imports: typing.Dict[str, str] = { + "EventSource": "._api", + "SSEError": "._exceptions", + "ServerSentEvent": "._models", + "aconnect_sse": "._api", + "connect_sse": "._api", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["EventSource", "SSEError", "ServerSentEvent", "aconnect_sse", "connect_sse"] diff --git a/src/intercom/core/http_sse/_api.py b/src/intercom/core/http_sse/_api.py new file mode 100644 index 00000000..f900b3b6 --- /dev/null +++ b/src/intercom/core/http_sse/_api.py @@ -0,0 +1,112 @@ +# This file was auto-generated by Fern from our API Definition. + +import re +from contextlib import asynccontextmanager, contextmanager +from typing import Any, AsyncGenerator, AsyncIterator, Iterator, cast + +import httpx +from ._decoders import SSEDecoder +from ._exceptions import SSEError +from ._models import ServerSentEvent + + +class EventSource: + def __init__(self, response: httpx.Response) -> None: + self._response = response + + def _check_content_type(self) -> None: + content_type = self._response.headers.get("content-type", "").partition(";")[0] + if "text/event-stream" not in content_type: + raise SSEError( + f"Expected response header Content-Type to contain 'text/event-stream', got {content_type!r}" + ) + + def _get_charset(self) -> str: + """Extract charset from Content-Type header, fallback to UTF-8.""" + content_type = self._response.headers.get("content-type", "") + + # Parse charset parameter using regex + charset_match = re.search(r"charset=([^;\s]+)", content_type, re.IGNORECASE) + if charset_match: + charset = charset_match.group(1).strip("\"'") + # Validate that it's a known encoding + try: + # Test if the charset is valid by trying to encode/decode + "test".encode(charset).decode(charset) + return charset + except (LookupError, UnicodeError): + # If charset is invalid, fall back to UTF-8 + pass + + # Default to UTF-8 if no charset specified or invalid charset + return "utf-8" + + @property + def response(self) -> httpx.Response: + return self._response + + def iter_sse(self) -> Iterator[ServerSentEvent]: + self._check_content_type() + decoder = SSEDecoder() + charset = self._get_charset() + + buffer = "" + for chunk in self._response.iter_bytes(): + # Decode chunk using detected charset + text_chunk = chunk.decode(charset, errors="replace") + buffer += text_chunk + + # Process complete lines + while "\n" in buffer: + line, buffer = buffer.split("\n", 1) + line = line.rstrip("\r") + sse = decoder.decode(line) + # when we reach a "\n\n" => line = '' + # => decoder will attempt to return an SSE Event + if sse is not None: + yield sse + + # Process any remaining data in buffer + if buffer.strip(): + line = buffer.rstrip("\r") + sse = decoder.decode(line) + if sse is not None: + yield sse + + async def aiter_sse(self) -> AsyncGenerator[ServerSentEvent, None]: + self._check_content_type() + decoder = SSEDecoder() + lines = cast(AsyncGenerator[str, None], self._response.aiter_lines()) + try: + async for line in lines: + line = line.rstrip("\n") + sse = decoder.decode(line) + if sse is not None: + yield sse + finally: + await lines.aclose() + + +@contextmanager +def connect_sse(client: httpx.Client, method: str, url: str, **kwargs: Any) -> Iterator[EventSource]: + headers = kwargs.pop("headers", {}) + headers["Accept"] = "text/event-stream" + headers["Cache-Control"] = "no-store" + + with client.stream(method, url, headers=headers, **kwargs) as response: + yield EventSource(response) + + +@asynccontextmanager +async def aconnect_sse( + client: httpx.AsyncClient, + method: str, + url: str, + **kwargs: Any, +) -> AsyncIterator[EventSource]: + headers = kwargs.pop("headers", {}) + headers["Accept"] = "text/event-stream" + headers["Cache-Control"] = "no-store" + + async with client.stream(method, url, headers=headers, **kwargs) as response: + yield EventSource(response) diff --git a/src/intercom/core/http_sse/_decoders.py b/src/intercom/core/http_sse/_decoders.py new file mode 100644 index 00000000..339b0890 --- /dev/null +++ b/src/intercom/core/http_sse/_decoders.py @@ -0,0 +1,61 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import List, Optional + +from ._models import ServerSentEvent + + +class SSEDecoder: + def __init__(self) -> None: + self._event = "" + self._data: List[str] = [] + self._last_event_id = "" + self._retry: Optional[int] = None + + def decode(self, line: str) -> Optional[ServerSentEvent]: + # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501 + + if not line: + if not self._event and not self._data and not self._last_event_id and self._retry is None: + return None + + sse = ServerSentEvent( + event=self._event, + data="\n".join(self._data), + id=self._last_event_id, + retry=self._retry, + ) + + # NOTE: as per the SSE spec, do not reset last_event_id. + self._event = "" + self._data = [] + self._retry = None + + return sse + + if line.startswith(":"): + return None + + fieldname, _, value = line.partition(":") + + if value.startswith(" "): + value = value[1:] + + if fieldname == "event": + self._event = value + elif fieldname == "data": + self._data.append(value) + elif fieldname == "id": + if "\0" in value: + pass + else: + self._last_event_id = value + elif fieldname == "retry": + try: + self._retry = int(value) + except (TypeError, ValueError): + pass + else: + pass # Field is ignored. + + return None diff --git a/src/intercom/core/http_sse/_exceptions.py b/src/intercom/core/http_sse/_exceptions.py new file mode 100644 index 00000000..81605a8a --- /dev/null +++ b/src/intercom/core/http_sse/_exceptions.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import httpx + + +class SSEError(httpx.TransportError): + pass diff --git a/src/intercom/core/http_sse/_models.py b/src/intercom/core/http_sse/_models.py new file mode 100644 index 00000000..1af57f8f --- /dev/null +++ b/src/intercom/core/http_sse/_models.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import json +from dataclasses import dataclass +from typing import Any, Optional + + +@dataclass(frozen=True) +class ServerSentEvent: + event: str = "message" + data: str = "" + id: str = "" + retry: Optional[int] = None + + def json(self) -> Any: + """Parse the data field as JSON.""" + return json.loads(self.data) diff --git a/src/intercom/core/jsonable_encoder.py b/src/intercom/core/jsonable_encoder.py new file mode 100644 index 00000000..afee3662 --- /dev/null +++ b/src/intercom/core/jsonable_encoder.py @@ -0,0 +1,100 @@ +# This file was auto-generated by Fern from our API Definition. + +""" +jsonable_encoder converts a Python object to a JSON-friendly dict +(e.g. datetimes to strings, Pydantic models to dicts). + +Taken from FastAPI, and made a bit simpler +https://github.com/tiangolo/fastapi/blob/master/fastapi/encoders.py +""" + +import base64 +import dataclasses +import datetime as dt +from enum import Enum +from pathlib import PurePath +from types import GeneratorType +from typing import Any, Callable, Dict, List, Optional, Set, Union + +import pydantic +from .datetime_utils import serialize_datetime +from .pydantic_utilities import ( + IS_PYDANTIC_V2, + encode_by_type, + to_jsonable_with_fallback, +) + +SetIntStr = Set[Union[int, str]] +DictIntStrAny = Dict[Union[int, str], Any] + + +def jsonable_encoder(obj: Any, custom_encoder: Optional[Dict[Any, Callable[[Any], Any]]] = None) -> Any: + custom_encoder = custom_encoder or {} + if custom_encoder: + if type(obj) in custom_encoder: + return custom_encoder[type(obj)](obj) + else: + for encoder_type, encoder_instance in custom_encoder.items(): + if isinstance(obj, encoder_type): + return encoder_instance(obj) + if isinstance(obj, pydantic.BaseModel): + if IS_PYDANTIC_V2: + encoder = getattr(obj.model_config, "json_encoders", {}) # type: ignore # Pydantic v2 + else: + encoder = getattr(obj.__config__, "json_encoders", {}) # type: ignore # Pydantic v1 + if custom_encoder: + encoder.update(custom_encoder) + obj_dict = obj.dict(by_alias=True) + if "__root__" in obj_dict: + obj_dict = obj_dict["__root__"] + if "root" in obj_dict: + obj_dict = obj_dict["root"] + return jsonable_encoder(obj_dict, custom_encoder=encoder) + if dataclasses.is_dataclass(obj): + obj_dict = dataclasses.asdict(obj) # type: ignore + return jsonable_encoder(obj_dict, custom_encoder=custom_encoder) + if isinstance(obj, bytes): + return base64.b64encode(obj).decode("utf-8") + if isinstance(obj, Enum): + return obj.value + if isinstance(obj, PurePath): + return str(obj) + if isinstance(obj, (str, int, float, type(None))): + return obj + if isinstance(obj, dt.datetime): + return serialize_datetime(obj) + if isinstance(obj, dt.date): + return str(obj) + if isinstance(obj, dict): + encoded_dict = {} + allowed_keys = set(obj.keys()) + for key, value in obj.items(): + if key in allowed_keys: + encoded_key = jsonable_encoder(key, custom_encoder=custom_encoder) + encoded_value = jsonable_encoder(value, custom_encoder=custom_encoder) + encoded_dict[encoded_key] = encoded_value + return encoded_dict + if isinstance(obj, (list, set, frozenset, GeneratorType, tuple)): + encoded_list = [] + for item in obj: + encoded_list.append(jsonable_encoder(item, custom_encoder=custom_encoder)) + return encoded_list + + def fallback_serializer(o: Any) -> Any: + attempt_encode = encode_by_type(o) + if attempt_encode is not None: + return attempt_encode + + try: + data = dict(o) + except Exception as e: + errors: List[Exception] = [] + errors.append(e) + try: + data = vars(o) + except Exception as e: + errors.append(e) + raise ValueError(errors) from e + return jsonable_encoder(data, custom_encoder=custom_encoder) + + return to_jsonable_with_fallback(obj, fallback_serializer) diff --git a/src/intercom/core/pagination.py b/src/intercom/core/pagination.py new file mode 100644 index 00000000..760b0899 --- /dev/null +++ b/src/intercom/core/pagination.py @@ -0,0 +1,82 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +from dataclasses import dataclass +from typing import AsyncIterator, Awaitable, Callable, Generic, Iterator, List, Optional, TypeVar + +# Generic to represent the underlying type of the results within a page +T = TypeVar("T") +# Generic to represent the type of the API response +R = TypeVar("R") + + +# SDKs implement a Page ABC per-pagination request, the endpoint then returns a pager that wraps this type +# for example, an endpoint will return SyncPager[UserPage] where UserPage implements the Page ABC. ex: +# +# SyncPager( +# has_next=response.list_metadata.after is not None, +# items=response.data, +# # This should be the outer function that returns the SyncPager again +# get_next=lambda: list(..., cursor: response.cursor) (or list(..., offset: offset + 1)) +# ) + + +@dataclass(frozen=True) +class SyncPager(Generic[T, R]): + get_next: Optional[Callable[[], Optional[SyncPager[T, R]]]] + has_next: bool + items: Optional[List[T]] + response: R + + # Here we type ignore the iterator to avoid a mypy error + # caused by the type conflict with Pydanitc's __iter__ method + # brought in by extending the base model + def __iter__(self) -> Iterator[T]: # type: ignore[override] + for page in self.iter_pages(): + if page.items is not None: + yield from page.items + + def iter_pages(self) -> Iterator[SyncPager[T, R]]: + page: Optional[SyncPager[T, R]] = self + while page is not None: + yield page + + if not page.has_next or page.get_next is None: + return + + page = page.get_next() + if page is None or page.items is None or len(page.items) == 0: + return + + def next_page(self) -> Optional[SyncPager[T, R]]: + return self.get_next() if self.get_next is not None else None + + +@dataclass(frozen=True) +class AsyncPager(Generic[T, R]): + get_next: Optional[Callable[[], Awaitable[Optional[AsyncPager[T, R]]]]] + has_next: bool + items: Optional[List[T]] + response: R + + async def __aiter__(self) -> AsyncIterator[T]: + async for page in self.iter_pages(): + if page.items is not None: + for item in page.items: + yield item + + async def iter_pages(self) -> AsyncIterator[AsyncPager[T, R]]: + page: Optional[AsyncPager[T, R]] = self + while page is not None: + yield page + + if not page.has_next or page.get_next is None: + return + + page = await page.get_next() + if page is None or page.items is None or len(page.items) == 0: + return + + async def next_page(self) -> Optional[AsyncPager[T, R]]: + return await self.get_next() if self.get_next is not None else None diff --git a/src/intercom/core/pydantic_utilities.py b/src/intercom/core/pydantic_utilities.py new file mode 100644 index 00000000..185e5c4f --- /dev/null +++ b/src/intercom/core/pydantic_utilities.py @@ -0,0 +1,260 @@ +# This file was auto-generated by Fern from our API Definition. + +# nopycln: file +import datetime as dt +from collections import defaultdict +from typing import Any, Callable, ClassVar, Dict, List, Mapping, Optional, Set, Tuple, Type, TypeVar, Union, cast + +import pydantic + +IS_PYDANTIC_V2 = pydantic.VERSION.startswith("2.") + +if IS_PYDANTIC_V2: + from pydantic.v1.datetime_parse import parse_date as parse_date + from pydantic.v1.datetime_parse import parse_datetime as parse_datetime + from pydantic.v1.fields import ModelField as ModelField + from pydantic.v1.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[attr-defined] + from pydantic.v1.typing import get_args as get_args + from pydantic.v1.typing import get_origin as get_origin + from pydantic.v1.typing import is_literal_type as is_literal_type + from pydantic.v1.typing import is_union as is_union +else: + from pydantic.datetime_parse import parse_date as parse_date # type: ignore[no-redef] + from pydantic.datetime_parse import parse_datetime as parse_datetime # type: ignore[no-redef] + from pydantic.fields import ModelField as ModelField # type: ignore[attr-defined, no-redef] + from pydantic.json import ENCODERS_BY_TYPE as encoders_by_type # type: ignore[no-redef] + from pydantic.typing import get_args as get_args # type: ignore[no-redef] + from pydantic.typing import get_origin as get_origin # type: ignore[no-redef] + from pydantic.typing import is_literal_type as is_literal_type # type: ignore[no-redef] + from pydantic.typing import is_union as is_union # type: ignore[no-redef] + +from .datetime_utils import serialize_datetime +from .serialization import convert_and_respect_annotation_metadata +from typing_extensions import TypeAlias + +T = TypeVar("T") +Model = TypeVar("Model", bound=pydantic.BaseModel) + + +def parse_obj_as(type_: Type[T], object_: Any) -> T: + dealiased_object = convert_and_respect_annotation_metadata(object_=object_, annotation=type_, direction="read") + if IS_PYDANTIC_V2: + adapter = pydantic.TypeAdapter(type_) # type: ignore[attr-defined] + return adapter.validate_python(dealiased_object) + return pydantic.parse_obj_as(type_, dealiased_object) + + +def to_jsonable_with_fallback(obj: Any, fallback_serializer: Callable[[Any], Any]) -> Any: + if IS_PYDANTIC_V2: + from pydantic_core import to_jsonable_python + + return to_jsonable_python(obj, fallback=fallback_serializer) + return fallback_serializer(obj) + + +class UniversalBaseModel(pydantic.BaseModel): + if IS_PYDANTIC_V2: + model_config: ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict( # type: ignore[typeddict-unknown-key] + # Allow fields beginning with `model_` to be used in the model + protected_namespaces=(), + ) + + @pydantic.model_serializer(mode="plain", when_used="json") # type: ignore[attr-defined] + def serialize_model(self) -> Any: # type: ignore[name-defined] + serialized = self.dict() # type: ignore[attr-defined] + data = {k: serialize_datetime(v) if isinstance(v, dt.datetime) else v for k, v in serialized.items()} + return data + + else: + + class Config: + smart_union = True + json_encoders = {dt.datetime: serialize_datetime} + + @classmethod + def model_construct(cls: Type["Model"], _fields_set: Optional[Set[str]] = None, **values: Any) -> "Model": + dealiased_object = convert_and_respect_annotation_metadata(object_=values, annotation=cls, direction="read") + return cls.construct(_fields_set, **dealiased_object) + + @classmethod + def construct(cls: Type["Model"], _fields_set: Optional[Set[str]] = None, **values: Any) -> "Model": + dealiased_object = convert_and_respect_annotation_metadata(object_=values, annotation=cls, direction="read") + if IS_PYDANTIC_V2: + return super().model_construct(_fields_set, **dealiased_object) # type: ignore[misc] + return super().construct(_fields_set, **dealiased_object) + + def json(self, **kwargs: Any) -> str: + kwargs_with_defaults = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + if IS_PYDANTIC_V2: + return super().model_dump_json(**kwargs_with_defaults) # type: ignore[misc] + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: Any) -> Dict[str, Any]: + """ + Override the default dict method to `exclude_unset` by default. This function patches + `exclude_unset` to work include fields within non-None default values. + """ + # Note: the logic here is multiplexed given the levers exposed in Pydantic V1 vs V2 + # Pydantic V1's .dict can be extremely slow, so we do not want to call it twice. + # + # We'd ideally do the same for Pydantic V2, but it shells out to a library to serialize models + # that we have less control over, and this is less intrusive than custom serializers for now. + if IS_PYDANTIC_V2: + kwargs_with_defaults_exclude_unset = { + **kwargs, + "by_alias": True, + "exclude_unset": True, + "exclude_none": False, + } + kwargs_with_defaults_exclude_none = { + **kwargs, + "by_alias": True, + "exclude_none": True, + "exclude_unset": False, + } + dict_dump = deep_union_pydantic_dicts( + super().model_dump(**kwargs_with_defaults_exclude_unset), # type: ignore[misc] + super().model_dump(**kwargs_with_defaults_exclude_none), # type: ignore[misc] + ) + + else: + _fields_set = self.__fields_set__.copy() + + fields = _get_model_fields(self.__class__) + for name, field in fields.items(): + if name not in _fields_set: + default = _get_field_default(field) + + # If the default values are non-null act like they've been set + # This effectively allows exclude_unset to work like exclude_none where + # the latter passes through intentionally set none values. + if default is not None or ("exclude_unset" in kwargs and not kwargs["exclude_unset"]): + _fields_set.add(name) + + if default is not None: + self.__fields_set__.add(name) + + kwargs_with_defaults_exclude_unset_include_fields = { + "by_alias": True, + "exclude_unset": True, + "include": _fields_set, + **kwargs, + } + + dict_dump = super().dict(**kwargs_with_defaults_exclude_unset_include_fields) + + return cast( + Dict[str, Any], + convert_and_respect_annotation_metadata(object_=dict_dump, annotation=self.__class__, direction="write"), + ) + + +def _union_list_of_pydantic_dicts(source: List[Any], destination: List[Any]) -> List[Any]: + converted_list: List[Any] = [] + for i, item in enumerate(source): + destination_value = destination[i] + if isinstance(item, dict): + converted_list.append(deep_union_pydantic_dicts(item, destination_value)) + elif isinstance(item, list): + converted_list.append(_union_list_of_pydantic_dicts(item, destination_value)) + else: + converted_list.append(item) + return converted_list + + +def deep_union_pydantic_dicts(source: Dict[str, Any], destination: Dict[str, Any]) -> Dict[str, Any]: + for key, value in source.items(): + node = destination.setdefault(key, {}) + if isinstance(value, dict): + deep_union_pydantic_dicts(value, node) + # Note: we do not do this same processing for sets given we do not have sets of models + # and given the sets are unordered, the processing of the set and matching objects would + # be non-trivial. + elif isinstance(value, list): + destination[key] = _union_list_of_pydantic_dicts(value, node) + else: + destination[key] = value + + return destination + + +if IS_PYDANTIC_V2: + + class V2RootModel(UniversalBaseModel, pydantic.RootModel): # type: ignore[misc, name-defined, type-arg] + pass + + UniversalRootModel: TypeAlias = V2RootModel # type: ignore[misc] +else: + UniversalRootModel: TypeAlias = UniversalBaseModel # type: ignore[misc, no-redef] + + +def encode_by_type(o: Any) -> Any: + encoders_by_class_tuples: Dict[Callable[[Any], Any], Tuple[Any, ...]] = defaultdict(tuple) + for type_, encoder in encoders_by_type.items(): + encoders_by_class_tuples[encoder] += (type_,) + + if type(o) in encoders_by_type: + return encoders_by_type[type(o)](o) + for encoder, classes_tuple in encoders_by_class_tuples.items(): + if isinstance(o, classes_tuple): + return encoder(o) + + +def update_forward_refs(model: Type["Model"], **localns: Any) -> None: + if IS_PYDANTIC_V2: + model.model_rebuild(raise_errors=False) # type: ignore[attr-defined] + else: + model.update_forward_refs(**localns) + + +# Mirrors Pydantic's internal typing +AnyCallable = Callable[..., Any] + + +def universal_root_validator( + pre: bool = False, +) -> Callable[[AnyCallable], AnyCallable]: + def decorator(func: AnyCallable) -> AnyCallable: + if IS_PYDANTIC_V2: + # In Pydantic v2, for RootModel we always use "before" mode + # The custom validators transform the input value before the model is created + return cast(AnyCallable, pydantic.model_validator(mode="before")(func)) # type: ignore[attr-defined] + return cast(AnyCallable, pydantic.root_validator(pre=pre)(func)) # type: ignore[call-overload] + + return decorator + + +def universal_field_validator(field_name: str, pre: bool = False) -> Callable[[AnyCallable], AnyCallable]: + def decorator(func: AnyCallable) -> AnyCallable: + if IS_PYDANTIC_V2: + return cast(AnyCallable, pydantic.field_validator(field_name, mode="before" if pre else "after")(func)) # type: ignore[attr-defined] + return cast(AnyCallable, pydantic.validator(field_name, pre=pre)(func)) + + return decorator + + +PydanticField = Union[ModelField, pydantic.fields.FieldInfo] + + +def _get_model_fields(model: Type["Model"]) -> Mapping[str, PydanticField]: + if IS_PYDANTIC_V2: + return cast(Mapping[str, PydanticField], model.model_fields) # type: ignore[attr-defined] + return cast(Mapping[str, PydanticField], model.__fields__) + + +def _get_field_default(field: PydanticField) -> Any: + try: + value = field.get_default() # type: ignore[union-attr] + except: + value = field.default + if IS_PYDANTIC_V2: + from pydantic_core import PydanticUndefined + + if value == PydanticUndefined: + return None + return value + return value diff --git a/src/intercom/core/query_encoder.py b/src/intercom/core/query_encoder.py new file mode 100644 index 00000000..3183001d --- /dev/null +++ b/src/intercom/core/query_encoder.py @@ -0,0 +1,58 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict, List, Optional, Tuple + +import pydantic + + +# Flattens dicts to be of the form {"key[subkey][subkey2]": value} where value is not a dict +def traverse_query_dict(dict_flat: Dict[str, Any], key_prefix: Optional[str] = None) -> List[Tuple[str, Any]]: + result = [] + for k, v in dict_flat.items(): + key = f"{key_prefix}[{k}]" if key_prefix is not None else k + if isinstance(v, dict): + result.extend(traverse_query_dict(v, key)) + elif isinstance(v, list): + for arr_v in v: + if isinstance(arr_v, dict): + result.extend(traverse_query_dict(arr_v, key)) + else: + result.append((key, arr_v)) + else: + result.append((key, v)) + return result + + +def single_query_encoder(query_key: str, query_value: Any) -> List[Tuple[str, Any]]: + if isinstance(query_value, pydantic.BaseModel) or isinstance(query_value, dict): + if isinstance(query_value, pydantic.BaseModel): + obj_dict = query_value.dict(by_alias=True) + else: + obj_dict = query_value + return traverse_query_dict(obj_dict, query_key) + elif isinstance(query_value, list): + encoded_values: List[Tuple[str, Any]] = [] + for value in query_value: + if isinstance(value, pydantic.BaseModel) or isinstance(value, dict): + if isinstance(value, pydantic.BaseModel): + obj_dict = value.dict(by_alias=True) + elif isinstance(value, dict): + obj_dict = value + + encoded_values.extend(single_query_encoder(query_key, obj_dict)) + else: + encoded_values.append((query_key, value)) + + return encoded_values + + return [(query_key, query_value)] + + +def encode_query(query: Optional[Dict[str, Any]]) -> Optional[List[Tuple[str, Any]]]: + if query is None: + return None + + encoded_query = [] + for k, v in query.items(): + encoded_query.extend(single_query_encoder(k, v)) + return encoded_query diff --git a/src/intercom/core/remove_none_from_dict.py b/src/intercom/core/remove_none_from_dict.py new file mode 100644 index 00000000..c2298143 --- /dev/null +++ b/src/intercom/core/remove_none_from_dict.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, Dict, Mapping, Optional + + +def remove_none_from_dict(original: Mapping[str, Optional[Any]]) -> Dict[str, Any]: + new: Dict[str, Any] = {} + for key, value in original.items(): + if value is not None: + new[key] = value + return new diff --git a/src/intercom/core/request_options.py b/src/intercom/core/request_options.py new file mode 100644 index 00000000..1b388044 --- /dev/null +++ b/src/intercom/core/request_options.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +try: + from typing import NotRequired # type: ignore +except ImportError: + from typing_extensions import NotRequired + + +class RequestOptions(typing.TypedDict, total=False): + """ + Additional options for request-specific configuration when calling APIs via the SDK. + This is used primarily as an optional final parameter for service functions. + + Attributes: + - timeout_in_seconds: int. The number of seconds to await an API call before timing out. + + - max_retries: int. The max number of retries to attempt if the API call fails. + + - additional_headers: typing.Dict[str, typing.Any]. A dictionary containing additional parameters to spread into the request's header dict + + - additional_query_parameters: typing.Dict[str, typing.Any]. A dictionary containing additional parameters to spread into the request's query parameters dict + + - additional_body_parameters: typing.Dict[str, typing.Any]. A dictionary containing additional parameters to spread into the request's body parameters dict + + - chunk_size: int. The size, in bytes, to process each chunk of data being streamed back within the response. This equates to leveraging `chunk_size` within `requests` or `httpx`, and is only leveraged for file downloads. + """ + + timeout_in_seconds: NotRequired[int] + max_retries: NotRequired[int] + additional_headers: NotRequired[typing.Dict[str, typing.Any]] + additional_query_parameters: NotRequired[typing.Dict[str, typing.Any]] + additional_body_parameters: NotRequired[typing.Dict[str, typing.Any]] + chunk_size: NotRequired[int] diff --git a/src/intercom/core/serialization.py b/src/intercom/core/serialization.py new file mode 100644 index 00000000..c36e865c --- /dev/null +++ b/src/intercom/core/serialization.py @@ -0,0 +1,276 @@ +# This file was auto-generated by Fern from our API Definition. + +import collections +import inspect +import typing + +import pydantic +import typing_extensions + + +class FieldMetadata: + """ + Metadata class used to annotate fields to provide additional information. + + Example: + class MyDict(TypedDict): + field: typing.Annotated[str, FieldMetadata(alias="field_name")] + + Will serialize: `{"field": "value"}` + To: `{"field_name": "value"}` + """ + + alias: str + + def __init__(self, *, alias: str) -> None: + self.alias = alias + + +def convert_and_respect_annotation_metadata( + *, + object_: typing.Any, + annotation: typing.Any, + inner_type: typing.Optional[typing.Any] = None, + direction: typing.Literal["read", "write"], +) -> typing.Any: + """ + Respect the metadata annotations on a field, such as aliasing. This function effectively + manipulates the dict-form of an object to respect the metadata annotations. This is primarily used for + TypedDicts, which cannot support aliasing out of the box, and can be extended for additional + utilities, such as defaults. + + Parameters + ---------- + object_ : typing.Any + + annotation : type + The type we're looking to apply typing annotations from + + inner_type : typing.Optional[type] + + Returns + ------- + typing.Any + """ + + if object_ is None: + return None + if inner_type is None: + inner_type = annotation + + clean_type = _remove_annotations(inner_type) + # Pydantic models + if ( + inspect.isclass(clean_type) + and issubclass(clean_type, pydantic.BaseModel) + and isinstance(object_, typing.Mapping) + ): + return _convert_mapping(object_, clean_type, direction) + # TypedDicts + if typing_extensions.is_typeddict(clean_type) and isinstance(object_, typing.Mapping): + return _convert_mapping(object_, clean_type, direction) + + if ( + typing_extensions.get_origin(clean_type) == typing.Dict + or typing_extensions.get_origin(clean_type) == dict + or clean_type == typing.Dict + ) and isinstance(object_, typing.Dict): + key_type = typing_extensions.get_args(clean_type)[0] + value_type = typing_extensions.get_args(clean_type)[1] + + return { + key: convert_and_respect_annotation_metadata( + object_=value, + annotation=annotation, + inner_type=value_type, + direction=direction, + ) + for key, value in object_.items() + } + + # If you're iterating on a string, do not bother to coerce it to a sequence. + if not isinstance(object_, str): + if ( + typing_extensions.get_origin(clean_type) == typing.Set + or typing_extensions.get_origin(clean_type) == set + or clean_type == typing.Set + ) and isinstance(object_, typing.Set): + inner_type = typing_extensions.get_args(clean_type)[0] + return { + convert_and_respect_annotation_metadata( + object_=item, + annotation=annotation, + inner_type=inner_type, + direction=direction, + ) + for item in object_ + } + elif ( + ( + typing_extensions.get_origin(clean_type) == typing.List + or typing_extensions.get_origin(clean_type) == list + or clean_type == typing.List + ) + and isinstance(object_, typing.List) + ) or ( + ( + typing_extensions.get_origin(clean_type) == typing.Sequence + or typing_extensions.get_origin(clean_type) == collections.abc.Sequence + or clean_type == typing.Sequence + ) + and isinstance(object_, typing.Sequence) + ): + inner_type = typing_extensions.get_args(clean_type)[0] + return [ + convert_and_respect_annotation_metadata( + object_=item, + annotation=annotation, + inner_type=inner_type, + direction=direction, + ) + for item in object_ + ] + + if typing_extensions.get_origin(clean_type) == typing.Union: + # We should be able to ~relatively~ safely try to convert keys against all + # member types in the union, the edge case here is if one member aliases a field + # of the same name to a different name from another member + # Or if another member aliases a field of the same name that another member does not. + for member in typing_extensions.get_args(clean_type): + object_ = convert_and_respect_annotation_metadata( + object_=object_, + annotation=annotation, + inner_type=member, + direction=direction, + ) + return object_ + + annotated_type = _get_annotation(annotation) + if annotated_type is None: + return object_ + + # If the object is not a TypedDict, a Union, or other container (list, set, sequence, etc.) + # Then we can safely call it on the recursive conversion. + return object_ + + +def _convert_mapping( + object_: typing.Mapping[str, object], + expected_type: typing.Any, + direction: typing.Literal["read", "write"], +) -> typing.Mapping[str, object]: + converted_object: typing.Dict[str, object] = {} + try: + annotations = typing_extensions.get_type_hints(expected_type, include_extras=True) + except NameError: + # The TypedDict contains a circular reference, so + # we use the __annotations__ attribute directly. + annotations = getattr(expected_type, "__annotations__", {}) + aliases_to_field_names = _get_alias_to_field_name(annotations) + for key, value in object_.items(): + if direction == "read" and key in aliases_to_field_names: + dealiased_key = aliases_to_field_names.get(key) + if dealiased_key is not None: + type_ = annotations.get(dealiased_key) + else: + type_ = annotations.get(key) + # Note you can't get the annotation by the field name if you're in read mode, so you must check the aliases map + # + # So this is effectively saying if we're in write mode, and we don't have a type, or if we're in read mode and we don't have an alias + # then we can just pass the value through as is + if type_ is None: + converted_object[key] = value + elif direction == "read" and key not in aliases_to_field_names: + converted_object[key] = convert_and_respect_annotation_metadata( + object_=value, annotation=type_, direction=direction + ) + else: + converted_object[_alias_key(key, type_, direction, aliases_to_field_names)] = ( + convert_and_respect_annotation_metadata(object_=value, annotation=type_, direction=direction) + ) + return converted_object + + +def _get_annotation(type_: typing.Any) -> typing.Optional[typing.Any]: + maybe_annotated_type = typing_extensions.get_origin(type_) + if maybe_annotated_type is None: + return None + + if maybe_annotated_type == typing_extensions.NotRequired: + type_ = typing_extensions.get_args(type_)[0] + maybe_annotated_type = typing_extensions.get_origin(type_) + + if maybe_annotated_type == typing_extensions.Annotated: + return type_ + + return None + + +def _remove_annotations(type_: typing.Any) -> typing.Any: + maybe_annotated_type = typing_extensions.get_origin(type_) + if maybe_annotated_type is None: + return type_ + + if maybe_annotated_type == typing_extensions.NotRequired: + return _remove_annotations(typing_extensions.get_args(type_)[0]) + + if maybe_annotated_type == typing_extensions.Annotated: + return _remove_annotations(typing_extensions.get_args(type_)[0]) + + return type_ + + +def get_alias_to_field_mapping(type_: typing.Any) -> typing.Dict[str, str]: + annotations = typing_extensions.get_type_hints(type_, include_extras=True) + return _get_alias_to_field_name(annotations) + + +def get_field_to_alias_mapping(type_: typing.Any) -> typing.Dict[str, str]: + annotations = typing_extensions.get_type_hints(type_, include_extras=True) + return _get_field_to_alias_name(annotations) + + +def _get_alias_to_field_name( + field_to_hint: typing.Dict[str, typing.Any], +) -> typing.Dict[str, str]: + aliases = {} + for field, hint in field_to_hint.items(): + maybe_alias = _get_alias_from_type(hint) + if maybe_alias is not None: + aliases[maybe_alias] = field + return aliases + + +def _get_field_to_alias_name( + field_to_hint: typing.Dict[str, typing.Any], +) -> typing.Dict[str, str]: + aliases = {} + for field, hint in field_to_hint.items(): + maybe_alias = _get_alias_from_type(hint) + if maybe_alias is not None: + aliases[field] = maybe_alias + return aliases + + +def _get_alias_from_type(type_: typing.Any) -> typing.Optional[str]: + maybe_annotated_type = _get_annotation(type_) + + if maybe_annotated_type is not None: + # The actual annotations are 1 onward, the first is the annotated type + annotations = typing_extensions.get_args(maybe_annotated_type)[1:] + + for annotation in annotations: + if isinstance(annotation, FieldMetadata) and annotation.alias is not None: + return annotation.alias + return None + + +def _alias_key( + key: str, + type_: typing.Any, + direction: typing.Literal["read", "write"], + aliases_to_field_names: typing.Dict[str, str], +) -> str: + if direction == "read": + return aliases_to_field_names.get(key, key) + return _get_alias_from_type(type_=type_) or key diff --git a/src/intercom/core/unchecked_base_model.py b/src/intercom/core/unchecked_base_model.py new file mode 100644 index 00000000..0a645e41 --- /dev/null +++ b/src/intercom/core/unchecked_base_model.py @@ -0,0 +1,396 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import inspect +import typing +import uuid + +import pydantic +import typing_extensions +from .pydantic_utilities import ( + IS_PYDANTIC_V2, + ModelField, + UniversalBaseModel, + get_args, + get_origin, + is_literal_type, + is_union, + parse_date, + parse_datetime, + parse_obj_as, +) +from .serialization import convert_and_respect_annotation_metadata, get_field_to_alias_mapping +from pydantic_core import PydanticUndefined + + +class UnionMetadata: + discriminant: str + + def __init__(self, *, discriminant: str) -> None: + self.discriminant = discriminant + + +Model = typing.TypeVar("Model", bound=pydantic.BaseModel) + + +class UncheckedBaseModel(UniversalBaseModel): + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow") # type: ignore # Pydantic v2 + else: + + class Config: + extra = pydantic.Extra.allow + + if IS_PYDANTIC_V2: + + @classmethod + def model_validate( + cls: typing.Type["Model"], + obj: typing.Any, + *args: typing.Any, + **kwargs: typing.Any, + ) -> "Model": + """ + Ensure that when using Pydantic v2's `model_validate` entrypoint we still + respect our FieldMetadata-based aliasing. + """ + dealiased_obj = convert_and_respect_annotation_metadata( + object_=obj, + annotation=cls, + direction="read", + ) + return super().model_validate(dealiased_obj, *args, **kwargs) # type: ignore[misc] + + @classmethod + def model_construct( + cls: typing.Type["Model"], + _fields_set: typing.Optional[typing.Set[str]] = None, + **values: typing.Any, + ) -> "Model": + # Fallback construct function to the specified override below. + return cls.construct(_fields_set=_fields_set, **values) + + # Allow construct to not validate model + # Implementation taken from: https://github.com/pydantic/pydantic/issues/1168#issuecomment-817742836 + @classmethod + def construct( + cls: typing.Type["Model"], + _fields_set: typing.Optional[typing.Set[str]] = None, + **values: typing.Any, + ) -> "Model": + m = cls.__new__(cls) + fields_values = {} + + if _fields_set is None: + _fields_set = set(values.keys()) + + fields = _get_model_fields(cls) + populate_by_name = _get_is_populate_by_name(cls) + field_aliases = get_field_to_alias_mapping(cls) + + for name, field in fields.items(): + # Key here is only used to pull data from the values dict + # you should always use the NAME of the field to for field_values, etc. + # because that's how the object is constructed from a pydantic perspective + key = field.alias + if (key is None or field.alias == name) and name in field_aliases: + key = field_aliases[name] + + if key is None or (key not in values and populate_by_name): # Added this to allow population by field name + key = name + + if key in values: + if IS_PYDANTIC_V2: + type_ = field.annotation # type: ignore # Pydantic v2 + else: + type_ = typing.cast(typing.Type, field.outer_type_) # type: ignore # Pydantic < v1.10.15 + + fields_values[name] = ( + construct_type(object_=values[key], type_=type_) if type_ is not None else values[key] + ) + _fields_set.add(name) + else: + default = _get_field_default(field) + fields_values[name] = default + + # If the default values are non-null act like they've been set + # This effectively allows exclude_unset to work like exclude_none where + # the latter passes through intentionally set none values. + if default != None and default != PydanticUndefined: + _fields_set.add(name) + + # Add extras back in + extras = {} + pydantic_alias_fields = [field.alias for field in fields.values()] + internal_alias_fields = list(field_aliases.values()) + for key, value in values.items(): + # If the key is not a field by name, nor an alias to a field, then it's extra + if (key not in pydantic_alias_fields and key not in internal_alias_fields) and key not in fields: + if IS_PYDANTIC_V2: + extras[key] = value + else: + _fields_set.add(key) + fields_values[key] = value + + object.__setattr__(m, "__dict__", fields_values) + + if IS_PYDANTIC_V2: + object.__setattr__(m, "__pydantic_private__", None) + object.__setattr__(m, "__pydantic_extra__", extras) + object.__setattr__(m, "__pydantic_fields_set__", _fields_set) + else: + object.__setattr__(m, "__fields_set__", _fields_set) + m._init_private_attributes() # type: ignore # Pydantic v1 + return m + + +def _validate_collection_items_compatible(collection: typing.Any, target_type: typing.Type[typing.Any]) -> bool: + """ + Validate that all items in a collection are compatible with the target type. + + Args: + collection: The collection to validate (list, set, or dict values) + target_type: The target type to validate against + + Returns: + True if all items are compatible, False otherwise + """ + if inspect.isclass(target_type) and issubclass(target_type, pydantic.BaseModel): + for item in collection: + try: + # Try to validate the item against the target type + if isinstance(item, dict): + parse_obj_as(target_type, item) + else: + # If it's not a dict, it might already be the right type + if not isinstance(item, target_type): + return False + except Exception: + return False + return True + + +def _convert_undiscriminated_union_type(union_type: typing.Type[typing.Any], object_: typing.Any) -> typing.Any: + inner_types = get_args(union_type) + if typing.Any in inner_types: + return object_ + + for inner_type in inner_types: + # Handle lists of objects that need parsing + if get_origin(inner_type) is list and isinstance(object_, list): + list_inner_type = get_args(inner_type)[0] + try: + if inspect.isclass(list_inner_type) and issubclass(list_inner_type, pydantic.BaseModel): + # Validate that all items in the list are compatible with the target type + if _validate_collection_items_compatible(object_, list_inner_type): + parsed_list = [parse_obj_as(object_=item, type_=list_inner_type) for item in object_] + return parsed_list + except Exception: + pass + + try: + if inspect.isclass(inner_type) and issubclass(inner_type, pydantic.BaseModel): + # Attempt a validated parse until one works + return parse_obj_as(inner_type, object_) + except Exception: + continue + + # If none of the types work, try matching literal fields first, then fall back + # First pass: try types where all literal fields match the object's values + for inner_type in inner_types: + if inspect.isclass(inner_type) and issubclass(inner_type, pydantic.BaseModel): + fields = _get_model_fields(inner_type) + literal_fields_match = True + + for field_name, field in fields.items(): + # Check if this field has a Literal type + if IS_PYDANTIC_V2: + field_type = field.annotation # type: ignore # Pydantic v2 + else: + field_type = field.outer_type_ # type: ignore # Pydantic v1 + + if is_literal_type(field_type): # type: ignore[arg-type] + field_default = _get_field_default(field) + name_or_alias = get_field_to_alias_mapping(inner_type).get(field_name, field_name) + # Get the value from the object + if isinstance(object_, dict): + object_value = object_.get(name_or_alias) + else: + object_value = getattr(object_, name_or_alias, None) + + # If the literal field value doesn't match, this type is not a match + if object_value is not None and field_default != object_value: + literal_fields_match = False + break + + # If all literal fields match, try to construct this type + if literal_fields_match: + try: + return construct_type(object_=object_, type_=inner_type) + except Exception: + continue + + # Second pass: if no literal matches, just return the first successful cast + for inner_type in inner_types: + try: + return construct_type(object_=object_, type_=inner_type) + except Exception: + continue + + +def _convert_union_type(type_: typing.Type[typing.Any], object_: typing.Any) -> typing.Any: + base_type = get_origin(type_) or type_ + union_type = type_ + if base_type == typing_extensions.Annotated: # type: ignore[comparison-overlap] + union_type = get_args(type_)[0] + annotated_metadata = get_args(type_)[1:] + for metadata in annotated_metadata: + if isinstance(metadata, UnionMetadata): + try: + # Cast to the correct type, based on the discriminant + for inner_type in get_args(union_type): + try: + objects_discriminant = getattr(object_, metadata.discriminant) + except: + objects_discriminant = object_[metadata.discriminant] + if inner_type.__fields__[metadata.discriminant].default == objects_discriminant: + return construct_type(object_=object_, type_=inner_type) + except Exception: + # Allow to fall through to our regular union handling + pass + return _convert_undiscriminated_union_type(union_type, object_) + + +def construct_type(*, type_: typing.Type[typing.Any], object_: typing.Any) -> typing.Any: + """ + Here we are essentially creating the same `construct` method in spirit as the above, but for all types, not just + Pydantic models. + The idea is to essentially attempt to coerce object_ to type_ (recursively) + """ + # Short circuit when dealing with optionals, don't try to coerces None to a type + if object_ is None: + return None + + base_type = get_origin(type_) or type_ + is_annotated = base_type == typing_extensions.Annotated # type: ignore[comparison-overlap] + maybe_annotation_members = get_args(type_) + is_annotated_union = is_annotated and is_union(get_origin(maybe_annotation_members[0])) + + if base_type == typing.Any: # type: ignore[comparison-overlap] + return object_ + + if base_type == dict: + if not isinstance(object_, typing.Mapping): + return object_ + + key_type, items_type = get_args(type_) + d = { + construct_type(object_=key, type_=key_type): construct_type(object_=item, type_=items_type) + for key, item in object_.items() + } + return d + + if base_type == list: + if not isinstance(object_, list): + return object_ + + inner_type = get_args(type_)[0] + return [construct_type(object_=entry, type_=inner_type) for entry in object_] + + if base_type == set: + if not isinstance(object_, set) and not isinstance(object_, list): + return object_ + + inner_type = get_args(type_)[0] + return {construct_type(object_=entry, type_=inner_type) for entry in object_} + + if is_union(base_type) or is_annotated_union: + return _convert_union_type(type_, object_) + + # Cannot do an `issubclass` with a literal type, let's also just confirm we have a class before this call + if ( + object_ is not None + and not is_literal_type(type_) + and ( + (inspect.isclass(base_type) and issubclass(base_type, pydantic.BaseModel)) + or ( + is_annotated + and inspect.isclass(maybe_annotation_members[0]) + and issubclass(maybe_annotation_members[0], pydantic.BaseModel) + ) + ) + ): + if IS_PYDANTIC_V2: + return type_.model_construct(**object_) + else: + return type_.construct(**object_) + + if base_type == dt.datetime: + try: + return parse_datetime(object_) + except Exception: + return object_ + + if base_type == dt.date: + try: + return parse_date(object_) + except Exception: + return object_ + + if base_type == uuid.UUID: + try: + return uuid.UUID(object_) + except Exception: + return object_ + + if base_type == int: + try: + return int(object_) + except Exception: + return object_ + + if base_type == bool: + try: + if isinstance(object_, str): + stringified_object = object_.lower() + return stringified_object == "true" or stringified_object == "1" + + return bool(object_) + except Exception: + return object_ + + return object_ + + +def _get_is_populate_by_name(model: typing.Type["Model"]) -> bool: + if IS_PYDANTIC_V2: + return model.model_config.get("populate_by_name", False) # type: ignore # Pydantic v2 + return model.__config__.allow_population_by_field_name # type: ignore # Pydantic v1 + + +PydanticField = typing.Union[ModelField, pydantic.fields.FieldInfo] + + +# Pydantic V1 swapped the typing of __fields__'s values from ModelField to FieldInfo +# And so we try to handle both V1 cases, as well as V2 (FieldInfo from model.model_fields) +def _get_model_fields( + model: typing.Type["Model"], +) -> typing.Mapping[str, PydanticField]: + if IS_PYDANTIC_V2: + return model.model_fields # type: ignore # Pydantic v2 + else: + return model.__fields__ # type: ignore # Pydantic v1 + + +def _get_field_default(field: PydanticField) -> typing.Any: + try: + value = field.get_default() # type: ignore # Pydantic < v1.10.15 + except: + value = field.default + if IS_PYDANTIC_V2: + from pydantic_core import PydanticUndefined + + if value == PydanticUndefined: + return None + return value + return value diff --git a/src/intercom/custom_channel_events/__init__.py b/src/intercom/custom_channel_events/__init__.py new file mode 100644 index 00000000..5cde0202 --- /dev/null +++ b/src/intercom/custom_channel_events/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/src/intercom/custom_channel_events/client.py b/src/intercom/custom_channel_events/client.py new file mode 100644 index 00000000..7a98364d --- /dev/null +++ b/src/intercom/custom_channel_events/client.py @@ -0,0 +1,553 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.custom_channel_attribute import CustomChannelAttribute +from ..types.custom_channel_contact import CustomChannelContact +from ..types.custom_channel_notification_response import CustomChannelNotificationResponse +from .raw_client import AsyncRawCustomChannelEventsClient, RawCustomChannelEventsClient + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class CustomChannelEventsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawCustomChannelEventsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawCustomChannelEventsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawCustomChannelEventsClient + """ + return self._raw_client + + def notify_new_conversation( + self, + *, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomChannelNotificationResponse: + """ + Notifies Intercom that a new conversation was created in your custom channel/platform. This triggers conversation creation and workflow automations within Intercom for your custom channel integration. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomChannelNotificationResponse + Successfully notified Intercom + + Examples + -------- + from intercom import CustomChannelContact, Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.custom_channel_events.notify_new_conversation( + event_id="event_id", + external_conversation_id="external_conversation_id", + contact=CustomChannelContact( + type="user", + external_id="external_id", + ), + ) + """ + _response = self._raw_client.notify_new_conversation( + event_id=event_id, + external_conversation_id=external_conversation_id, + contact=contact, + request_options=request_options, + ) + return _response.data + + def notify_new_message( + self, + *, + body: str, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomChannelNotificationResponse: + """ + Notifies Intercom that a new message was sent in a conversation on your custom channel/platform. This allows Intercom to process the message and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + body : str + The message content sent by the user. + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomChannelNotificationResponse + Successfully notified Intercom + + Examples + -------- + from intercom import CustomChannelContact, Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.custom_channel_events.notify_new_message( + body="body", + event_id="event_id", + external_conversation_id="external_conversation_id", + contact=CustomChannelContact( + type="user", + external_id="external_id", + ), + ) + """ + _response = self._raw_client.notify_new_message( + body=body, + event_id=event_id, + external_conversation_id=external_conversation_id, + contact=contact, + request_options=request_options, + ) + return _response.data + + def notify_quick_reply_selected( + self, + *, + quick_reply_option_id: str, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomChannelNotificationResponse: + """ + Notifies Intercom that a user selected a quick reply option in your custom channel/platform. This allows Intercom to process the response and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + quick_reply_option_id : str + Id of the selected quick reply option. + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomChannelNotificationResponse + Successfully notified Intercom + + Examples + -------- + from intercom import CustomChannelContact, Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.custom_channel_events.notify_quick_reply_selected( + event_id="evt_67890", + external_conversation_id="conv_13579", + contact=CustomChannelContact( + type="user", + external_id="user_003", + name="Alice Example", + email="alice@example.com", + ), + quick_reply_option_id="1234", + ) + """ + _response = self._raw_client.notify_quick_reply_selected( + quick_reply_option_id=quick_reply_option_id, + event_id=event_id, + external_conversation_id=external_conversation_id, + contact=contact, + request_options=request_options, + ) + return _response.data + + def notify_attribute_collected( + self, + *, + attribute: CustomChannelAttribute, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomChannelNotificationResponse: + """ + Notifies Intercom that a user provided a response to an attribute collector in your custom channel/platform. This allows Intercom to process the attribute and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + attribute : CustomChannelAttribute + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomChannelNotificationResponse + Successfully notified Intercom + + Examples + -------- + from intercom import CustomChannelAttribute, CustomChannelContact, Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.custom_channel_events.notify_attribute_collected( + attribute=CustomChannelAttribute( + id="id", + value="value", + ), + event_id="event_id", + external_conversation_id="external_conversation_id", + contact=CustomChannelContact( + type="user", + external_id="external_id", + ), + ) + """ + _response = self._raw_client.notify_attribute_collected( + attribute=attribute, + event_id=event_id, + external_conversation_id=external_conversation_id, + contact=contact, + request_options=request_options, + ) + return _response.data + + +class AsyncCustomChannelEventsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawCustomChannelEventsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawCustomChannelEventsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawCustomChannelEventsClient + """ + return self._raw_client + + async def notify_new_conversation( + self, + *, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomChannelNotificationResponse: + """ + Notifies Intercom that a new conversation was created in your custom channel/platform. This triggers conversation creation and workflow automations within Intercom for your custom channel integration. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomChannelNotificationResponse + Successfully notified Intercom + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom, CustomChannelContact + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.custom_channel_events.notify_new_conversation( + event_id="event_id", + external_conversation_id="external_conversation_id", + contact=CustomChannelContact( + type="user", + external_id="external_id", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.notify_new_conversation( + event_id=event_id, + external_conversation_id=external_conversation_id, + contact=contact, + request_options=request_options, + ) + return _response.data + + async def notify_new_message( + self, + *, + body: str, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomChannelNotificationResponse: + """ + Notifies Intercom that a new message was sent in a conversation on your custom channel/platform. This allows Intercom to process the message and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + body : str + The message content sent by the user. + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomChannelNotificationResponse + Successfully notified Intercom + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom, CustomChannelContact + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.custom_channel_events.notify_new_message( + body="body", + event_id="event_id", + external_conversation_id="external_conversation_id", + contact=CustomChannelContact( + type="user", + external_id="external_id", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.notify_new_message( + body=body, + event_id=event_id, + external_conversation_id=external_conversation_id, + contact=contact, + request_options=request_options, + ) + return _response.data + + async def notify_quick_reply_selected( + self, + *, + quick_reply_option_id: str, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomChannelNotificationResponse: + """ + Notifies Intercom that a user selected a quick reply option in your custom channel/platform. This allows Intercom to process the response and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + quick_reply_option_id : str + Id of the selected quick reply option. + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomChannelNotificationResponse + Successfully notified Intercom + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom, CustomChannelContact + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.custom_channel_events.notify_quick_reply_selected( + event_id="evt_67890", + external_conversation_id="conv_13579", + contact=CustomChannelContact( + type="user", + external_id="user_003", + name="Alice Example", + email="alice@example.com", + ), + quick_reply_option_id="1234", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.notify_quick_reply_selected( + quick_reply_option_id=quick_reply_option_id, + event_id=event_id, + external_conversation_id=external_conversation_id, + contact=contact, + request_options=request_options, + ) + return _response.data + + async def notify_attribute_collected( + self, + *, + attribute: CustomChannelAttribute, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomChannelNotificationResponse: + """ + Notifies Intercom that a user provided a response to an attribute collector in your custom channel/platform. This allows Intercom to process the attribute and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + attribute : CustomChannelAttribute + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomChannelNotificationResponse + Successfully notified Intercom + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom, CustomChannelAttribute, CustomChannelContact + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.custom_channel_events.notify_attribute_collected( + attribute=CustomChannelAttribute( + id="id", + value="value", + ), + event_id="event_id", + external_conversation_id="external_conversation_id", + contact=CustomChannelContact( + type="user", + external_id="external_id", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.notify_attribute_collected( + attribute=attribute, + event_id=event_id, + external_conversation_id=external_conversation_id, + contact=contact, + request_options=request_options, + ) + return _response.data diff --git a/src/intercom/custom_channel_events/raw_client.py b/src/intercom/custom_channel_events/raw_client.py new file mode 100644 index 00000000..cf399764 --- /dev/null +++ b/src/intercom/custom_channel_events/raw_client.py @@ -0,0 +1,904 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.http_response import AsyncHttpResponse, HttpResponse +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.bad_request_error import BadRequestError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..errors.unprocessable_entity_error import UnprocessableEntityError +from ..types.custom_channel_attribute import CustomChannelAttribute +from ..types.custom_channel_contact import CustomChannelContact +from ..types.custom_channel_notification_response import CustomChannelNotificationResponse +from ..types.error import Error + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawCustomChannelEventsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def notify_new_conversation( + self, + *, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[CustomChannelNotificationResponse]: + """ + Notifies Intercom that a new conversation was created in your custom channel/platform. This triggers conversation creation and workflow automations within Intercom for your custom channel integration. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CustomChannelNotificationResponse] + Successfully notified Intercom + """ + _response = self._client_wrapper.httpx_client.request( + "custom_channel_events/notify_new_conversation", + method="POST", + json={ + "event_id": event_id, + "external_conversation_id": external_conversation_id, + "contact": convert_and_respect_annotation_metadata( + object_=contact, annotation=CustomChannelContact, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomChannelNotificationResponse, + construct_type( + type_=CustomChannelNotificationResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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 notify_new_message( + self, + *, + body: str, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[CustomChannelNotificationResponse]: + """ + Notifies Intercom that a new message was sent in a conversation on your custom channel/platform. This allows Intercom to process the message and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + body : str + The message content sent by the user. + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CustomChannelNotificationResponse] + Successfully notified Intercom + """ + _response = self._client_wrapper.httpx_client.request( + "custom_channel_events/notify_new_message", + method="POST", + json={ + "body": body, + "event_id": event_id, + "external_conversation_id": external_conversation_id, + "contact": convert_and_respect_annotation_metadata( + object_=contact, annotation=CustomChannelContact, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomChannelNotificationResponse, + construct_type( + type_=CustomChannelNotificationResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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 notify_quick_reply_selected( + self, + *, + quick_reply_option_id: str, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[CustomChannelNotificationResponse]: + """ + Notifies Intercom that a user selected a quick reply option in your custom channel/platform. This allows Intercom to process the response and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + quick_reply_option_id : str + Id of the selected quick reply option. + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CustomChannelNotificationResponse] + Successfully notified Intercom + """ + _response = self._client_wrapper.httpx_client.request( + "custom_channel_events/notify_quick_reply_selected", + method="POST", + json={ + "quick_reply_option_id": quick_reply_option_id, + "event_id": event_id, + "external_conversation_id": external_conversation_id, + "contact": convert_and_respect_annotation_metadata( + object_=contact, annotation=CustomChannelContact, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomChannelNotificationResponse, + construct_type( + type_=CustomChannelNotificationResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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 notify_attribute_collected( + self, + *, + attribute: CustomChannelAttribute, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[CustomChannelNotificationResponse]: + """ + Notifies Intercom that a user provided a response to an attribute collector in your custom channel/platform. This allows Intercom to process the attribute and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + attribute : CustomChannelAttribute + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CustomChannelNotificationResponse] + Successfully notified Intercom + """ + _response = self._client_wrapper.httpx_client.request( + "custom_channel_events/notify_attribute_collected", + method="POST", + json={ + "attribute": convert_and_respect_annotation_metadata( + object_=attribute, annotation=CustomChannelAttribute, direction="write" + ), + "event_id": event_id, + "external_conversation_id": external_conversation_id, + "contact": convert_and_respect_annotation_metadata( + object_=contact, annotation=CustomChannelContact, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomChannelNotificationResponse, + construct_type( + type_=CustomChannelNotificationResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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) + + +class AsyncRawCustomChannelEventsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def notify_new_conversation( + self, + *, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[CustomChannelNotificationResponse]: + """ + Notifies Intercom that a new conversation was created in your custom channel/platform. This triggers conversation creation and workflow automations within Intercom for your custom channel integration. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CustomChannelNotificationResponse] + Successfully notified Intercom + """ + _response = await self._client_wrapper.httpx_client.request( + "custom_channel_events/notify_new_conversation", + method="POST", + json={ + "event_id": event_id, + "external_conversation_id": external_conversation_id, + "contact": convert_and_respect_annotation_metadata( + object_=contact, annotation=CustomChannelContact, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomChannelNotificationResponse, + construct_type( + type_=CustomChannelNotificationResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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 notify_new_message( + self, + *, + body: str, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[CustomChannelNotificationResponse]: + """ + Notifies Intercom that a new message was sent in a conversation on your custom channel/platform. This allows Intercom to process the message and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + body : str + The message content sent by the user. + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CustomChannelNotificationResponse] + Successfully notified Intercom + """ + _response = await self._client_wrapper.httpx_client.request( + "custom_channel_events/notify_new_message", + method="POST", + json={ + "body": body, + "event_id": event_id, + "external_conversation_id": external_conversation_id, + "contact": convert_and_respect_annotation_metadata( + object_=contact, annotation=CustomChannelContact, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomChannelNotificationResponse, + construct_type( + type_=CustomChannelNotificationResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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 notify_quick_reply_selected( + self, + *, + quick_reply_option_id: str, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[CustomChannelNotificationResponse]: + """ + Notifies Intercom that a user selected a quick reply option in your custom channel/platform. This allows Intercom to process the response and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + quick_reply_option_id : str + Id of the selected quick reply option. + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CustomChannelNotificationResponse] + Successfully notified Intercom + """ + _response = await self._client_wrapper.httpx_client.request( + "custom_channel_events/notify_quick_reply_selected", + method="POST", + json={ + "quick_reply_option_id": quick_reply_option_id, + "event_id": event_id, + "external_conversation_id": external_conversation_id, + "contact": convert_and_respect_annotation_metadata( + object_=contact, annotation=CustomChannelContact, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomChannelNotificationResponse, + construct_type( + type_=CustomChannelNotificationResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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 notify_attribute_collected( + self, + *, + attribute: CustomChannelAttribute, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[CustomChannelNotificationResponse]: + """ + Notifies Intercom that a user provided a response to an attribute collector in your custom channel/platform. This allows Intercom to process the attribute and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + attribute : CustomChannelAttribute + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CustomChannelNotificationResponse] + Successfully notified Intercom + """ + _response = await self._client_wrapper.httpx_client.request( + "custom_channel_events/notify_attribute_collected", + method="POST", + json={ + "attribute": convert_and_respect_annotation_metadata( + object_=attribute, annotation=CustomChannelAttribute, direction="write" + ), + "event_id": event_id, + "external_conversation_id": external_conversation_id, + "contact": convert_and_respect_annotation_metadata( + object_=contact, annotation=CustomChannelContact, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomChannelNotificationResponse, + construct_type( + type_=CustomChannelNotificationResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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) diff --git a/src/intercom/custom_object_instances/__init__.py b/src/intercom/custom_object_instances/__init__.py new file mode 100644 index 00000000..f9a5fd47 --- /dev/null +++ b/src/intercom/custom_object_instances/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import CustomObjectInstance +_dynamic_imports: typing.Dict[str, str] = {"CustomObjectInstance": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["CustomObjectInstance"] diff --git a/src/intercom/custom_object_instances/client.py b/src/intercom/custom_object_instances/client.py new file mode 100644 index 00000000..319b09f4 --- /dev/null +++ b/src/intercom/custom_object_instances/client.py @@ -0,0 +1,556 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.custom_object_instance_deleted import CustomObjectInstanceDeleted +from .raw_client import AsyncRawCustomObjectInstancesClient, RawCustomObjectInstancesClient +from .types.custom_object_instance import CustomObjectInstance + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class CustomObjectInstancesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawCustomObjectInstancesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawCustomObjectInstancesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawCustomObjectInstancesClient + """ + return self._raw_client + + def get_custom_object_instances_by_external_id( + self, + custom_object_type_identifier: str, + *, + external_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[CustomObjectInstance]: + """ + Fetch a Custom Object Instance by external_id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[CustomObjectInstance] + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.custom_object_instances.get_custom_object_instances_by_external_id( + custom_object_type_identifier="Order", + external_id="external_id", + ) + """ + _response = self._raw_client.get_custom_object_instances_by_external_id( + custom_object_type_identifier, external_id=external_id, request_options=request_options + ) + return _response.data + + def create_custom_object_instances( + self, + custom_object_type_identifier: str, + *, + external_id: typing.Optional[str] = OMIT, + external_created_at: typing.Optional[int] = OMIT, + external_updated_at: typing.Optional[int] = OMIT, + custom_attributes: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[CustomObjectInstance]: + """ + Create or update a custom object instance + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : typing.Optional[str] + A unique identifier for the Custom Object instance in the external system it originated from. + + external_created_at : typing.Optional[int] + The time when the Custom Object instance was created in the external system it originated from. + + external_updated_at : typing.Optional[int] + The time when the Custom Object instance was last updated in the external system it originated from. + + custom_attributes : typing.Optional[typing.Dict[str, typing.Optional[str]]] + The custom attributes which are set for the Custom Object instance. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[CustomObjectInstance] + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.custom_object_instances.create_custom_object_instances( + custom_object_type_identifier="Order", + external_id="123", + external_created_at=1392036272, + external_updated_at=1392036272, + custom_attributes={ + "order_number": "ORDER-12345", + "total_amount": "custom_attributes", + }, + ) + """ + _response = self._raw_client.create_custom_object_instances( + custom_object_type_identifier, + external_id=external_id, + external_created_at=external_created_at, + external_updated_at=external_updated_at, + custom_attributes=custom_attributes, + request_options=request_options, + ) + return _response.data + + def delete_custom_object_instances_by_id( + self, + custom_object_type_identifier: str, + *, + external_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomObjectInstanceDeleted: + """ + Delete a single Custom Object instance by external_id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomObjectInstanceDeleted + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.custom_object_instances.delete_custom_object_instances_by_id( + custom_object_type_identifier="Order", + external_id="external_id", + ) + """ + _response = self._raw_client.delete_custom_object_instances_by_id( + custom_object_type_identifier, external_id=external_id, request_options=request_options + ) + return _response.data + + def get_custom_object_instances_by_id( + self, + custom_object_type_identifier: str, + custom_object_instance_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[CustomObjectInstance]: + """ + Fetch a Custom Object Instance by id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + custom_object_instance_id : str + The id or external_id of the custom object instance + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[CustomObjectInstance] + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.custom_object_instances.get_custom_object_instances_by_id( + custom_object_type_identifier="Order", + custom_object_instance_id="custom_object_instance_id", + ) + """ + _response = self._raw_client.get_custom_object_instances_by_id( + custom_object_type_identifier, custom_object_instance_id, request_options=request_options + ) + return _response.data + + def delete_custom_object_instances_by_external_id( + self, + custom_object_type_identifier: str, + custom_object_instance_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomObjectInstanceDeleted: + """ + Delete a single Custom Object instance using the Intercom defined id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + custom_object_instance_id : str + The Intercom defined id of the custom object instance + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomObjectInstanceDeleted + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.custom_object_instances.delete_custom_object_instances_by_external_id( + custom_object_type_identifier="Order", + custom_object_instance_id="custom_object_instance_id", + ) + """ + _response = self._raw_client.delete_custom_object_instances_by_external_id( + custom_object_type_identifier, custom_object_instance_id, request_options=request_options + ) + return _response.data + + +class AsyncCustomObjectInstancesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawCustomObjectInstancesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawCustomObjectInstancesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawCustomObjectInstancesClient + """ + return self._raw_client + + async def get_custom_object_instances_by_external_id( + self, + custom_object_type_identifier: str, + *, + external_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[CustomObjectInstance]: + """ + Fetch a Custom Object Instance by external_id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[CustomObjectInstance] + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.custom_object_instances.get_custom_object_instances_by_external_id( + custom_object_type_identifier="Order", + external_id="external_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_custom_object_instances_by_external_id( + custom_object_type_identifier, external_id=external_id, request_options=request_options + ) + return _response.data + + async def create_custom_object_instances( + self, + custom_object_type_identifier: str, + *, + external_id: typing.Optional[str] = OMIT, + external_created_at: typing.Optional[int] = OMIT, + external_updated_at: typing.Optional[int] = OMIT, + custom_attributes: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[CustomObjectInstance]: + """ + Create or update a custom object instance + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : typing.Optional[str] + A unique identifier for the Custom Object instance in the external system it originated from. + + external_created_at : typing.Optional[int] + The time when the Custom Object instance was created in the external system it originated from. + + external_updated_at : typing.Optional[int] + The time when the Custom Object instance was last updated in the external system it originated from. + + custom_attributes : typing.Optional[typing.Dict[str, typing.Optional[str]]] + The custom attributes which are set for the Custom Object instance. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[CustomObjectInstance] + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.custom_object_instances.create_custom_object_instances( + custom_object_type_identifier="Order", + external_id="123", + external_created_at=1392036272, + external_updated_at=1392036272, + custom_attributes={ + "order_number": "ORDER-12345", + "total_amount": "custom_attributes", + }, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_custom_object_instances( + custom_object_type_identifier, + external_id=external_id, + external_created_at=external_created_at, + external_updated_at=external_updated_at, + custom_attributes=custom_attributes, + request_options=request_options, + ) + return _response.data + + async def delete_custom_object_instances_by_id( + self, + custom_object_type_identifier: str, + *, + external_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomObjectInstanceDeleted: + """ + Delete a single Custom Object instance by external_id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomObjectInstanceDeleted + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.custom_object_instances.delete_custom_object_instances_by_id( + custom_object_type_identifier="Order", + external_id="external_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_custom_object_instances_by_id( + custom_object_type_identifier, external_id=external_id, request_options=request_options + ) + return _response.data + + async def get_custom_object_instances_by_id( + self, + custom_object_type_identifier: str, + custom_object_instance_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[CustomObjectInstance]: + """ + Fetch a Custom Object Instance by id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + custom_object_instance_id : str + The id or external_id of the custom object instance + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[CustomObjectInstance] + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.custom_object_instances.get_custom_object_instances_by_id( + custom_object_type_identifier="Order", + custom_object_instance_id="custom_object_instance_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_custom_object_instances_by_id( + custom_object_type_identifier, custom_object_instance_id, request_options=request_options + ) + return _response.data + + async def delete_custom_object_instances_by_external_id( + self, + custom_object_type_identifier: str, + custom_object_instance_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomObjectInstanceDeleted: + """ + Delete a single Custom Object instance using the Intercom defined id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + custom_object_instance_id : str + The Intercom defined id of the custom object instance + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomObjectInstanceDeleted + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.custom_object_instances.delete_custom_object_instances_by_external_id( + custom_object_type_identifier="Order", + custom_object_instance_id="custom_object_instance_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_custom_object_instances_by_external_id( + custom_object_type_identifier, custom_object_instance_id, request_options=request_options + ) + return _response.data diff --git a/src/intercom/custom_object_instances/raw_client.py b/src/intercom/custom_object_instances/raw_client.py new file mode 100644 index 00000000..cdf05edc --- /dev/null +++ b/src/intercom/custom_object_instances/raw_client.py @@ -0,0 +1,773 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ..core.unchecked_base_model import construct_type +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.custom_object_instance_deleted import CustomObjectInstanceDeleted +from ..types.error import Error +from .types.custom_object_instance import CustomObjectInstance + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawCustomObjectInstancesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get_custom_object_instances_by_external_id( + self, + custom_object_type_identifier: str, + *, + external_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[typing.Optional[CustomObjectInstance]]: + """ + Fetch a Custom Object Instance by external_id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[CustomObjectInstance]] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"custom_object_instances/{jsonable_encoder(custom_object_type_identifier)}", + method="GET", + params={ + "external_id": external_id, + }, + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[CustomObjectInstance], + construct_type( + type_=typing.Optional[CustomObjectInstance], # 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 create_custom_object_instances( + self, + custom_object_type_identifier: str, + *, + external_id: typing.Optional[str] = OMIT, + external_created_at: typing.Optional[int] = OMIT, + external_updated_at: typing.Optional[int] = OMIT, + custom_attributes: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[typing.Optional[CustomObjectInstance]]: + """ + Create or update a custom object instance + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : typing.Optional[str] + A unique identifier for the Custom Object instance in the external system it originated from. + + external_created_at : typing.Optional[int] + The time when the Custom Object instance was created in the external system it originated from. + + external_updated_at : typing.Optional[int] + The time when the Custom Object instance was last updated in the external system it originated from. + + custom_attributes : typing.Optional[typing.Dict[str, typing.Optional[str]]] + The custom attributes which are set for the Custom Object instance. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[CustomObjectInstance]] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"custom_object_instances/{jsonable_encoder(custom_object_type_identifier)}", + method="POST", + json={ + "external_id": external_id, + "external_created_at": external_created_at, + "external_updated_at": external_updated_at, + "custom_attributes": custom_attributes, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[CustomObjectInstance], + construct_type( + type_=typing.Optional[CustomObjectInstance], # 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 delete_custom_object_instances_by_id( + self, + custom_object_type_identifier: str, + *, + external_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[CustomObjectInstanceDeleted]: + """ + Delete a single Custom Object instance by external_id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CustomObjectInstanceDeleted] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"custom_object_instances/{jsonable_encoder(custom_object_type_identifier)}", + method="DELETE", + params={ + "external_id": external_id, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomObjectInstanceDeleted, + construct_type( + type_=CustomObjectInstanceDeleted, # 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 get_custom_object_instances_by_id( + self, + custom_object_type_identifier: str, + custom_object_instance_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[typing.Optional[CustomObjectInstance]]: + """ + Fetch a Custom Object Instance by id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + custom_object_instance_id : str + The id or external_id of the custom object instance + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[CustomObjectInstance]] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"custom_object_instances/{jsonable_encoder(custom_object_type_identifier)}/{jsonable_encoder(custom_object_instance_id)}", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[CustomObjectInstance], + construct_type( + type_=typing.Optional[CustomObjectInstance], # 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 delete_custom_object_instances_by_external_id( + self, + custom_object_type_identifier: str, + custom_object_instance_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[CustomObjectInstanceDeleted]: + """ + Delete a single Custom Object instance using the Intercom defined id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + custom_object_instance_id : str + The Intercom defined id of the custom object instance + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CustomObjectInstanceDeleted] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"custom_object_instances/{jsonable_encoder(custom_object_type_identifier)}/{jsonable_encoder(custom_object_instance_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomObjectInstanceDeleted, + construct_type( + type_=CustomObjectInstanceDeleted, # 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) + + +class AsyncRawCustomObjectInstancesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get_custom_object_instances_by_external_id( + self, + custom_object_type_identifier: str, + *, + external_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[typing.Optional[CustomObjectInstance]]: + """ + Fetch a Custom Object Instance by external_id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[CustomObjectInstance]] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"custom_object_instances/{jsonable_encoder(custom_object_type_identifier)}", + method="GET", + params={ + "external_id": external_id, + }, + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[CustomObjectInstance], + construct_type( + type_=typing.Optional[CustomObjectInstance], # 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 create_custom_object_instances( + self, + custom_object_type_identifier: str, + *, + external_id: typing.Optional[str] = OMIT, + external_created_at: typing.Optional[int] = OMIT, + external_updated_at: typing.Optional[int] = OMIT, + custom_attributes: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[typing.Optional[CustomObjectInstance]]: + """ + Create or update a custom object instance + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : typing.Optional[str] + A unique identifier for the Custom Object instance in the external system it originated from. + + external_created_at : typing.Optional[int] + The time when the Custom Object instance was created in the external system it originated from. + + external_updated_at : typing.Optional[int] + The time when the Custom Object instance was last updated in the external system it originated from. + + custom_attributes : typing.Optional[typing.Dict[str, typing.Optional[str]]] + The custom attributes which are set for the Custom Object instance. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[CustomObjectInstance]] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"custom_object_instances/{jsonable_encoder(custom_object_type_identifier)}", + method="POST", + json={ + "external_id": external_id, + "external_created_at": external_created_at, + "external_updated_at": external_updated_at, + "custom_attributes": custom_attributes, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[CustomObjectInstance], + construct_type( + type_=typing.Optional[CustomObjectInstance], # 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 delete_custom_object_instances_by_id( + self, + custom_object_type_identifier: str, + *, + external_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[CustomObjectInstanceDeleted]: + """ + Delete a single Custom Object instance by external_id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CustomObjectInstanceDeleted] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"custom_object_instances/{jsonable_encoder(custom_object_type_identifier)}", + method="DELETE", + params={ + "external_id": external_id, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomObjectInstanceDeleted, + construct_type( + type_=CustomObjectInstanceDeleted, # 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 get_custom_object_instances_by_id( + self, + custom_object_type_identifier: str, + custom_object_instance_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[typing.Optional[CustomObjectInstance]]: + """ + Fetch a Custom Object Instance by id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + custom_object_instance_id : str + The id or external_id of the custom object instance + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[CustomObjectInstance]] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"custom_object_instances/{jsonable_encoder(custom_object_type_identifier)}/{jsonable_encoder(custom_object_instance_id)}", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[CustomObjectInstance], + construct_type( + type_=typing.Optional[CustomObjectInstance], # 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 delete_custom_object_instances_by_external_id( + self, + custom_object_type_identifier: str, + custom_object_instance_id: str, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[CustomObjectInstanceDeleted]: + """ + Delete a single Custom Object instance using the Intercom defined id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + custom_object_instance_id : str + The Intercom defined id of the custom object instance + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CustomObjectInstanceDeleted] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"custom_object_instances/{jsonable_encoder(custom_object_type_identifier)}/{jsonable_encoder(custom_object_instance_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomObjectInstanceDeleted, + construct_type( + type_=CustomObjectInstanceDeleted, # 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) diff --git a/src/intercom/custom_object_instances/types/__init__.py b/src/intercom/custom_object_instances/types/__init__.py new file mode 100644 index 00000000..bba7d61b --- /dev/null +++ b/src/intercom/custom_object_instances/types/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .custom_object_instance import CustomObjectInstance +_dynamic_imports: typing.Dict[str, str] = {"CustomObjectInstance": ".custom_object_instance"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["CustomObjectInstance"] diff --git a/src/intercom/custom_object_instances/types/custom_object_instance.py b/src/intercom/custom_object_instances/types/custom_object_instance.py new file mode 100644 index 00000000..8285b265 --- /dev/null +++ b/src/intercom/custom_object_instances/types/custom_object_instance.py @@ -0,0 +1,62 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CustomObjectInstance(UncheckedBaseModel): + """ + A Custom Object Instance represents an instance of a custom object type. This allows you to create and set custom attributes to store data about your customers that is not already captured by Intercom. The parent object includes recommended default attributes and you can add your own custom attributes. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom defined id representing the custom object instance. + """ + + external_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id you have defined for the custom object instance. + """ + + external_created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the Custom Object instance was created in the external system it originated from. + """ + + external_updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the Custom Object instance was last updated in the external system it originated from. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the attribute was created as a UTC Unix timestamp + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the attribute was last updated as a UTC Unix timestamp + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + The identifier of the custom object type that defines the structure of the custom object instance. + """ + + custom_attributes: typing.Optional[typing.Dict[str, str]] = pydantic.Field(default=None) + """ + The custom attributes you have set on the custom object instance. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/data_attributes/__init__.py b/src/intercom/data_attributes/__init__.py new file mode 100644 index 00000000..1df62abd --- /dev/null +++ b/src/intercom/data_attributes/__init__.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import DataAttribute, DataAttributeDataType, DataAttributeModel, DataAttributesListRequestModel +_dynamic_imports: typing.Dict[str, str] = { + "DataAttribute": ".types", + "DataAttributeDataType": ".types", + "DataAttributeModel": ".types", + "DataAttributesListRequestModel": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["DataAttribute", "DataAttributeDataType", "DataAttributeModel", "DataAttributesListRequestModel"] diff --git a/src/intercom/data_attributes/client.py b/src/intercom/data_attributes/client.py new file mode 100644 index 00000000..afddd86f --- /dev/null +++ b/src/intercom/data_attributes/client.py @@ -0,0 +1,341 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.create_data_attribute_request import CreateDataAttributeRequest +from ..types.data_attribute_list import DataAttributeList +from ..types.update_data_attribute_request_body import UpdateDataAttributeRequestBody +from .raw_client import AsyncRawDataAttributesClient, RawDataAttributesClient +from .types.data_attribute import DataAttribute +from .types.data_attributes_list_request_model import DataAttributesListRequestModel + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class DataAttributesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawDataAttributesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawDataAttributesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawDataAttributesClient + """ + return self._raw_client + + def list( + self, + *, + model: typing.Optional[DataAttributesListRequestModel] = None, + include_archived: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> DataAttributeList: + """ + You can fetch a list of all data attributes belonging to a workspace for contacts, companies or conversations. + + Parameters + ---------- + model : typing.Optional[DataAttributesListRequestModel] + Specify the data attribute model to return. + + include_archived : typing.Optional[bool] + Include archived attributes in the list. By default we return only non archived data attributes. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataAttributeList + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.data_attributes.list( + model="contact", + include_archived=True, + ) + """ + _response = self._raw_client.list( + model=model, include_archived=include_archived, request_options=request_options + ) + return _response.data + + def create( + self, *, request: CreateDataAttributeRequest, request_options: typing.Optional[RequestOptions] = None + ) -> DataAttribute: + """ + You can create a data attributes for a `contact` or a `company`. + + Parameters + ---------- + request : CreateDataAttributeRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataAttribute + Successful + + Examples + -------- + from intercom import CreateDataAttributeRequestOne, Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.data_attributes.create( + request=CreateDataAttributeRequestOne( + data_type="string", + ), + ) + """ + _response = self._raw_client.create(request=request, request_options=request_options) + return _response.data + + def update( + self, + data_attribute_id: int, + *, + request: UpdateDataAttributeRequestBody, + request_options: typing.Optional[RequestOptions] = None, + ) -> DataAttribute: + """ + + You can update a data attribute. + + > 🚧 Updating the data type is not possible + > + > It is currently a dangerous action to execute changing a data attribute's type via the API. You will need to update the type via the UI instead. + + Parameters + ---------- + data_attribute_id : int + The data attribute id + + request : UpdateDataAttributeRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataAttribute + Successful + + Examples + -------- + from intercom import ( + Intercom, + UpdateDataAttributeRequestOptions, + UpdateDataAttributeRequestOptionsOptionsItem, + ) + + client = Intercom( + token="YOUR_TOKEN", + ) + client.data_attributes.update( + data_attribute_id=1, + request=UpdateDataAttributeRequestOptions( + options=[ + UpdateDataAttributeRequestOptionsOptionsItem( + value="1-10", + ), + UpdateDataAttributeRequestOptionsOptionsItem( + value="11-20", + ), + ], + ), + ) + """ + _response = self._raw_client.update(data_attribute_id, request=request, request_options=request_options) + return _response.data + + +class AsyncDataAttributesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawDataAttributesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawDataAttributesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawDataAttributesClient + """ + return self._raw_client + + async def list( + self, + *, + model: typing.Optional[DataAttributesListRequestModel] = None, + include_archived: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> DataAttributeList: + """ + You can fetch a list of all data attributes belonging to a workspace for contacts, companies or conversations. + + Parameters + ---------- + model : typing.Optional[DataAttributesListRequestModel] + Specify the data attribute model to return. + + include_archived : typing.Optional[bool] + Include archived attributes in the list. By default we return only non archived data attributes. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataAttributeList + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.data_attributes.list( + model="contact", + include_archived=True, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list( + model=model, include_archived=include_archived, request_options=request_options + ) + return _response.data + + async def create( + self, *, request: CreateDataAttributeRequest, request_options: typing.Optional[RequestOptions] = None + ) -> DataAttribute: + """ + You can create a data attributes for a `contact` or a `company`. + + Parameters + ---------- + request : CreateDataAttributeRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataAttribute + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom, CreateDataAttributeRequestOne + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.data_attributes.create( + request=CreateDataAttributeRequestOne( + data_type="string", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create(request=request, request_options=request_options) + return _response.data + + async def update( + self, + data_attribute_id: int, + *, + request: UpdateDataAttributeRequestBody, + request_options: typing.Optional[RequestOptions] = None, + ) -> DataAttribute: + """ + + You can update a data attribute. + + > 🚧 Updating the data type is not possible + > + > It is currently a dangerous action to execute changing a data attribute's type via the API. You will need to update the type via the UI instead. + + Parameters + ---------- + data_attribute_id : int + The data attribute id + + request : UpdateDataAttributeRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataAttribute + Successful + + Examples + -------- + import asyncio + + from intercom import ( + AsyncIntercom, + UpdateDataAttributeRequestOptions, + UpdateDataAttributeRequestOptionsOptionsItem, + ) + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.data_attributes.update( + data_attribute_id=1, + request=UpdateDataAttributeRequestOptions( + options=[ + UpdateDataAttributeRequestOptionsOptionsItem( + value="1-10", + ), + UpdateDataAttributeRequestOptionsOptionsItem( + value="11-20", + ), + ], + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update(data_attribute_id, request=request, request_options=request_options) + return _response.data diff --git a/src/intercom/data_attributes/raw_client.py b/src/intercom/data_attributes/raw_client.py new file mode 100644 index 00000000..cf73d8c2 --- /dev/null +++ b/src/intercom/data_attributes/raw_client.py @@ -0,0 +1,493 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ..core.serialization import convert_and_respect_annotation_metadata +from ..core.unchecked_base_model import construct_type +from ..errors.bad_request_error import BadRequestError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..errors.unprocessable_entity_error import UnprocessableEntityError +from ..types.create_data_attribute_request import CreateDataAttributeRequest +from ..types.data_attribute_list import DataAttributeList +from ..types.error import Error +from ..types.update_data_attribute_request_body import UpdateDataAttributeRequestBody +from .types.data_attribute import DataAttribute +from .types.data_attributes_list_request_model import DataAttributesListRequestModel + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawDataAttributesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list( + self, + *, + model: typing.Optional[DataAttributesListRequestModel] = None, + include_archived: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DataAttributeList]: + """ + You can fetch a list of all data attributes belonging to a workspace for contacts, companies or conversations. + + Parameters + ---------- + model : typing.Optional[DataAttributesListRequestModel] + Specify the data attribute model to return. + + include_archived : typing.Optional[bool] + Include archived attributes in the list. By default we return only non archived data attributes. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DataAttributeList] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "data_attributes", + method="GET", + params={ + "model": model, + "include_archived": include_archived, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataAttributeList, + construct_type( + type_=DataAttributeList, # 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 create( + self, *, request: CreateDataAttributeRequest, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DataAttribute]: + """ + You can create a data attributes for a `contact` or a `company`. + + Parameters + ---------- + request : CreateDataAttributeRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DataAttribute] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + "data_attributes", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateDataAttributeRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataAttribute, + construct_type( + type_=DataAttribute, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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, + data_attribute_id: int, + *, + request: UpdateDataAttributeRequestBody, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DataAttribute]: + """ + + You can update a data attribute. + + > 🚧 Updating the data type is not possible + > + > It is currently a dangerous action to execute changing a data attribute's type via the API. You will need to update the type via the UI instead. + + Parameters + ---------- + data_attribute_id : int + The data attribute id + + request : UpdateDataAttributeRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DataAttribute] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + f"data_attributes/{jsonable_encoder(data_attribute_id)}", + method="PUT", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=UpdateDataAttributeRequestBody, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataAttribute, + construct_type( + type_=DataAttribute, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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) + + +class AsyncRawDataAttributesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, + *, + model: typing.Optional[DataAttributesListRequestModel] = None, + include_archived: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DataAttributeList]: + """ + You can fetch a list of all data attributes belonging to a workspace for contacts, companies or conversations. + + Parameters + ---------- + model : typing.Optional[DataAttributesListRequestModel] + Specify the data attribute model to return. + + include_archived : typing.Optional[bool] + Include archived attributes in the list. By default we return only non archived data attributes. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DataAttributeList] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "data_attributes", + method="GET", + params={ + "model": model, + "include_archived": include_archived, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataAttributeList, + construct_type( + type_=DataAttributeList, # 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 create( + self, *, request: CreateDataAttributeRequest, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DataAttribute]: + """ + You can create a data attributes for a `contact` or a `company`. + + Parameters + ---------- + request : CreateDataAttributeRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DataAttribute] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + "data_attributes", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateDataAttributeRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataAttribute, + construct_type( + type_=DataAttribute, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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, + data_attribute_id: int, + *, + request: UpdateDataAttributeRequestBody, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DataAttribute]: + """ + + You can update a data attribute. + + > 🚧 Updating the data type is not possible + > + > It is currently a dangerous action to execute changing a data attribute's type via the API. You will need to update the type via the UI instead. + + Parameters + ---------- + data_attribute_id : int + The data attribute id + + request : UpdateDataAttributeRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DataAttribute] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"data_attributes/{jsonable_encoder(data_attribute_id)}", + method="PUT", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=UpdateDataAttributeRequestBody, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataAttribute, + construct_type( + type_=DataAttribute, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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) diff --git a/src/intercom/data_attributes/types/__init__.py b/src/intercom/data_attributes/types/__init__.py new file mode 100644 index 00000000..319da858 --- /dev/null +++ b/src/intercom/data_attributes/types/__init__.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .data_attribute import DataAttribute + from .data_attribute_data_type import DataAttributeDataType + from .data_attribute_model import DataAttributeModel + from .data_attributes_list_request_model import DataAttributesListRequestModel +_dynamic_imports: typing.Dict[str, str] = { + "DataAttribute": ".data_attribute", + "DataAttributeDataType": ".data_attribute_data_type", + "DataAttributeModel": ".data_attribute_model", + "DataAttributesListRequestModel": ".data_attributes_list_request_model", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["DataAttribute", "DataAttributeDataType", "DataAttributeModel", "DataAttributesListRequestModel"] diff --git a/src/intercom/data_attributes/types/data_attribute.py b/src/intercom/data_attributes/types/data_attribute.py new file mode 100644 index 00000000..47486dde --- /dev/null +++ b/src/intercom/data_attributes/types/data_attribute.py @@ -0,0 +1,109 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .data_attribute_data_type import DataAttributeDataType +from .data_attribute_model import DataAttributeModel + + +class DataAttribute(UncheckedBaseModel): + """ + Data Attributes are metadata used to describe your contact, company and conversation models. These include standard and custom attributes. By using the data attributes endpoint, you can get the global list of attributes for your workspace, as well as create and archive custom attributes. + """ + + type: typing.Literal["data_attribute"] = pydantic.Field(default="data_attribute") + """ + Value is `data_attribute`. + """ + + id: typing.Optional[int] = pydantic.Field(default=None) + """ + The unique identifier for the data attribute which is given by Intercom. Only available for custom attributes. + """ + + model: typing.Optional[DataAttributeModel] = pydantic.Field(default=None) + """ + Value is `contact` for user/lead attributes and `company` for company attributes. + """ + + name: str = pydantic.Field() + """ + Name of the attribute. + """ + + full_name: str = pydantic.Field() + """ + Full name of the attribute. Should match the name unless it's a nested attribute. We can split full_name on `.` to access nested user object values. + """ + + label: str = pydantic.Field() + """ + Readable name of the attribute (i.e. name you see in the UI) + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + Readable description of the attribute. + """ + + data_type: DataAttributeDataType = pydantic.Field() + """ + The data type of the attribute. + """ + + options: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + List of predefined options for attribute value. + """ + + api_writable: typing.Optional[bool] = pydantic.Field(default=None) + """ + Can this attribute be updated through API + """ + + messenger_writable: typing.Optional[bool] = pydantic.Field(default=None) + """ + Can this attribute be updated by the Messenger + """ + + ui_writable: typing.Optional[bool] = pydantic.Field(default=None) + """ + Can this attribute be updated in the UI + """ + + custom: typing.Optional[bool] = pydantic.Field(default=None) + """ + Set to true if this is a CDA + """ + + archived: typing.Optional[bool] = pydantic.Field(default=None) + """ + Is this attribute archived. (Only applicable to CDAs) + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the attribute was created as a UTC Unix timestamp + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the attribute was last updated as a UTC Unix timestamp + """ + + admin_id: typing.Optional[str] = pydantic.Field(default=None) + """ + Teammate who created the attribute. Only applicable to CDAs + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/data_attributes/types/data_attribute_data_type.py b/src/intercom/data_attributes/types/data_attribute_data_type.py new file mode 100644 index 00000000..f9c833fc --- /dev/null +++ b/src/intercom/data_attributes/types/data_attribute_data_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +DataAttributeDataType = typing.Union[typing.Literal["string", "integer", "float", "boolean", "date"], typing.Any] diff --git a/src/intercom/data_attributes/types/data_attribute_model.py b/src/intercom/data_attributes/types/data_attribute_model.py new file mode 100644 index 00000000..a16196e2 --- /dev/null +++ b/src/intercom/data_attributes/types/data_attribute_model.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +DataAttributeModel = typing.Union[typing.Literal["contact", "company"], typing.Any] diff --git a/src/intercom/data_attributes/types/data_attributes_list_request_model.py b/src/intercom/data_attributes/types/data_attributes_list_request_model.py new file mode 100644 index 00000000..c80d8a2c --- /dev/null +++ b/src/intercom/data_attributes/types/data_attributes_list_request_model.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +DataAttributesListRequestModel = typing.Union[typing.Literal["contact", "company", "conversation"], typing.Any] diff --git a/src/intercom/data_events/__init__.py b/src/intercom/data_events/__init__.py new file mode 100644 index 00000000..8c837b00 --- /dev/null +++ b/src/intercom/data_events/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import DataEvent +_dynamic_imports: typing.Dict[str, str] = {"DataEvent": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["DataEvent"] diff --git a/src/intercom/data_events/types/__init__.py b/src/intercom/data_events/types/__init__.py new file mode 100644 index 00000000..9202f9a4 --- /dev/null +++ b/src/intercom/data_events/types/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .data_event import DataEvent +_dynamic_imports: typing.Dict[str, str] = {"DataEvent": ".data_event"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["DataEvent"] diff --git a/src/intercom/data_events/types/data_event.py b/src/intercom/data_events/types/data_event.py new file mode 100644 index 00000000..db2acc09 --- /dev/null +++ b/src/intercom/data_events/types/data_event.py @@ -0,0 +1,62 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class DataEvent(UncheckedBaseModel): + """ + Data events are used to notify Intercom of changes to your data. + """ + + type: typing.Optional[typing.Literal["event"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + event_name: str = pydantic.Field() + """ + The name of the event that occurred. This is presented to your App's admins when filtering and creating segments - a good event name is typically a past tense 'verb-noun' combination, to improve readability, for example `updated-plan`. + """ + + created_at: int = pydantic.Field() + """ + The time the event occurred as a UTC Unix timestamp + """ + + user_id: typing.Optional[str] = pydantic.Field(default=None) + """ + Your identifier for the user. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + Your identifier for a lead or a user. + """ + + intercom_user_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom identifier for the user. + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + An email address for your user. An email should only be used where your application uses email to uniquely identify users. + """ + + metadata: typing.Optional[typing.Dict[str, str]] = pydantic.Field(default=None) + """ + Optional metadata about the event. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/data_export/__init__.py b/src/intercom/data_export/__init__.py new file mode 100644 index 00000000..0ae7684f --- /dev/null +++ b/src/intercom/data_export/__init__.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import DataExport, DataExportExportReportingDataResponse, DataExportStatus +_dynamic_imports: typing.Dict[str, str] = { + "DataExport": ".types", + "DataExportExportReportingDataResponse": ".types", + "DataExportStatus": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["DataExport", "DataExportExportReportingDataResponse", "DataExportStatus"] diff --git a/src/intercom/data_export/client.py b/src/intercom/data_export/client.py new file mode 100644 index 00000000..7b88e8eb --- /dev/null +++ b/src/intercom/data_export/client.py @@ -0,0 +1,574 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawDataExportClient, RawDataExportClient +from .types.data_export import DataExport +from .types.data_export_export_reporting_data_response import DataExportExportReportingDataResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class DataExportClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawDataExportClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawDataExportClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawDataExportClient + """ + return self._raw_client + + def export_reporting_data( + self, + job_identifier: str, + *, + app_id: str, + client_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> DataExportExportReportingDataResponse: + """ + Parameters + ---------- + job_identifier : str + Unique identifier of the job. + + app_id : str + The Intercom defined code of the workspace the company is associated to. + + client_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataExportExportReportingDataResponse + Job status returned successfully + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.data_export.export_reporting_data( + job_identifier="job_identifier", + app_id="app_id", + client_id="client_id", + ) + """ + _response = self._raw_client.export_reporting_data( + job_identifier, app_id=app_id, client_id=client_id, request_options=request_options + ) + return _response.data + + def download_reporting_data_export( + self, job_identifier: str, *, app_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + Download the data from a completed reporting data export job. + + > Octet header required + > + > You will have to specify the header Accept: `application/octet-stream` when hitting this endpoint. + + Parameters + ---------- + job_identifier : str + + app_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.data_export.download_reporting_data_export( + job_identifier="job_identifier", + app_id="app_id", + ) + """ + _response = self._raw_client.download_reporting_data_export( + job_identifier, app_id=app_id, request_options=request_options + ) + return _response.data + + def create( + self, *, created_at_after: int, created_at_before: int, request_options: typing.Optional[RequestOptions] = None + ) -> DataExport: + """ + To create your export job, you need to send a `POST` request to the export endpoint `https://api.intercom.io/export/content/data`. + + The only parameters you need to provide are the range of dates that you want exported. + + >🚧 Limit of one active job + > + > You can only have one active job per workspace. You will receive a HTTP status code of 429 with the message Exceeded rate limit of 1 pending message data export jobs if you attempt to create a second concurrent job. + + >❗️ Updated_at not included + > + > It should be noted that the timeframe only includes messages sent during the time period and not messages that were only updated during this period. For example, if a message was updated yesterday but sent two days ago, you would need to set the created_at_after date before the message was sent to include that in your retrieval job. + + >📘 Date ranges are inclusive + > + > Requesting data for 2018-06-01 until 2018-06-30 will get all data for those days including those specified - e.g. 2018-06-01 00:00:00 until 2018-06-30 23:59:99. + + Parameters + ---------- + created_at_after : int + The start date that you request data for. It must be formatted as a unix timestamp. + + created_at_before : int + The end date that you request data for. It must be formatted as a unix timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataExport + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.data_export.create( + created_at_after=1734519776, + created_at_before=1734537776, + ) + """ + _response = self._raw_client.create( + created_at_after=created_at_after, created_at_before=created_at_before, request_options=request_options + ) + return _response.data + + def find(self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None) -> DataExport: + """ + You can view the status of your job by sending a `GET` request to the URL + `https://api.intercom.io/export/content/data/{job_identifier}` - the `{job_identifier}` is the value returned in the response when you first created the export job. More on it can be seen in the Export Job Model. + + > 🚧 Jobs expire after two days + > All jobs that have completed processing (and are thus available to download from the provided URL) will have an expiry limit of two days from when the export ob completed. After this, the data will no longer be available. + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataExport + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.data_export.find( + job_identifier="job_identifier", + ) + """ + _response = self._raw_client.find(job_identifier, request_options=request_options) + return _response.data + + def cancel(self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None) -> DataExport: + """ + You can cancel your job + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataExport + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.data_export.cancel( + job_identifier="job_identifier", + ) + """ + _response = self._raw_client.cancel(job_identifier, request_options=request_options) + return _response.data + + def download(self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + When a job has a status of complete, and thus a filled download_url, you can download your data by hitting that provided URL, formatted like so: https://api.intercom.io/download/content/data/xyz1234. + + Your exported message data will be streamed continuously back down to you in a gzipped CSV format. + + > 📘 Octet header required + > + > You will have to specify the header Accept: `application/octet-stream` when hitting this endpoint. + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.data_export.download( + job_identifier="job_identifier", + ) + """ + _response = self._raw_client.download(job_identifier, request_options=request_options) + return _response.data + + +class AsyncDataExportClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawDataExportClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawDataExportClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawDataExportClient + """ + return self._raw_client + + async def export_reporting_data( + self, + job_identifier: str, + *, + app_id: str, + client_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> DataExportExportReportingDataResponse: + """ + Parameters + ---------- + job_identifier : str + Unique identifier of the job. + + app_id : str + The Intercom defined code of the workspace the company is associated to. + + client_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataExportExportReportingDataResponse + Job status returned successfully + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.data_export.export_reporting_data( + job_identifier="job_identifier", + app_id="app_id", + client_id="client_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.export_reporting_data( + job_identifier, app_id=app_id, client_id=client_id, request_options=request_options + ) + return _response.data + + async def download_reporting_data_export( + self, job_identifier: str, *, app_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + Download the data from a completed reporting data export job. + + > Octet header required + > + > You will have to specify the header Accept: `application/octet-stream` when hitting this endpoint. + + Parameters + ---------- + job_identifier : str + + app_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.data_export.download_reporting_data_export( + job_identifier="job_identifier", + app_id="app_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.download_reporting_data_export( + job_identifier, app_id=app_id, request_options=request_options + ) + return _response.data + + async def create( + self, *, created_at_after: int, created_at_before: int, request_options: typing.Optional[RequestOptions] = None + ) -> DataExport: + """ + To create your export job, you need to send a `POST` request to the export endpoint `https://api.intercom.io/export/content/data`. + + The only parameters you need to provide are the range of dates that you want exported. + + >🚧 Limit of one active job + > + > You can only have one active job per workspace. You will receive a HTTP status code of 429 with the message Exceeded rate limit of 1 pending message data export jobs if you attempt to create a second concurrent job. + + >❗️ Updated_at not included + > + > It should be noted that the timeframe only includes messages sent during the time period and not messages that were only updated during this period. For example, if a message was updated yesterday but sent two days ago, you would need to set the created_at_after date before the message was sent to include that in your retrieval job. + + >📘 Date ranges are inclusive + > + > Requesting data for 2018-06-01 until 2018-06-30 will get all data for those days including those specified - e.g. 2018-06-01 00:00:00 until 2018-06-30 23:59:99. + + Parameters + ---------- + created_at_after : int + The start date that you request data for. It must be formatted as a unix timestamp. + + created_at_before : int + The end date that you request data for. It must be formatted as a unix timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataExport + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.data_export.create( + created_at_after=1734519776, + created_at_before=1734537776, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + created_at_after=created_at_after, created_at_before=created_at_before, request_options=request_options + ) + return _response.data + + async def find(self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None) -> DataExport: + """ + You can view the status of your job by sending a `GET` request to the URL + `https://api.intercom.io/export/content/data/{job_identifier}` - the `{job_identifier}` is the value returned in the response when you first created the export job. More on it can be seen in the Export Job Model. + + > 🚧 Jobs expire after two days + > All jobs that have completed processing (and are thus available to download from the provided URL) will have an expiry limit of two days from when the export ob completed. After this, the data will no longer be available. + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataExport + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.data_export.find( + job_identifier="job_identifier", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.find(job_identifier, request_options=request_options) + return _response.data + + async def cancel( + self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DataExport: + """ + You can cancel your job + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataExport + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.data_export.cancel( + job_identifier="job_identifier", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.cancel(job_identifier, request_options=request_options) + return _response.data + + async def download(self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + When a job has a status of complete, and thus a filled download_url, you can download your data by hitting that provided URL, formatted like so: https://api.intercom.io/download/content/data/xyz1234. + + Your exported message data will be streamed continuously back down to you in a gzipped CSV format. + + > 📘 Octet header required + > + > You will have to specify the header Accept: `application/octet-stream` when hitting this endpoint. + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.data_export.download( + job_identifier="job_identifier", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.download(job_identifier, request_options=request_options) + return _response.data diff --git a/src/intercom/data_export/raw_client.py b/src/intercom/data_export/raw_client.py new file mode 100644 index 00000000..5e28ec0a --- /dev/null +++ b/src/intercom/data_export/raw_client.py @@ -0,0 +1,623 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ..core.unchecked_base_model import construct_type +from ..errors.not_found_error import NotFoundError +from .types.data_export import DataExport +from .types.data_export_export_reporting_data_response import DataExportExportReportingDataResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawDataExportClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def export_reporting_data( + self, + job_identifier: str, + *, + app_id: str, + client_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DataExportExportReportingDataResponse]: + """ + Parameters + ---------- + job_identifier : str + Unique identifier of the job. + + app_id : str + The Intercom defined code of the workspace the company is associated to. + + client_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DataExportExportReportingDataResponse] + Job status returned successfully + """ + _response = self._client_wrapper.httpx_client.request( + f"export/reporting_data/{jsonable_encoder(job_identifier)}", + method="GET", + params={ + "app_id": app_id, + "client_id": client_id, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataExportExportReportingDataResponse, + construct_type( + type_=DataExportExportReportingDataResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + 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 download_reporting_data_export( + self, job_identifier: str, *, app_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[None]: + """ + Download the data from a completed reporting data export job. + + > Octet header required + > + > You will have to specify the header Accept: `application/octet-stream` when hitting this endpoint. + + Parameters + ---------- + job_identifier : str + + app_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + f"download/reporting_data/{jsonable_encoder(job_identifier)}", + method="GET", + params={ + "app_id": app_id, + }, + headers={ + "Accept": "application/octet-stream", + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + 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 create( + self, *, created_at_after: int, created_at_before: int, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DataExport]: + """ + To create your export job, you need to send a `POST` request to the export endpoint `https://api.intercom.io/export/content/data`. + + The only parameters you need to provide are the range of dates that you want exported. + + >🚧 Limit of one active job + > + > You can only have one active job per workspace. You will receive a HTTP status code of 429 with the message Exceeded rate limit of 1 pending message data export jobs if you attempt to create a second concurrent job. + + >❗️ Updated_at not included + > + > It should be noted that the timeframe only includes messages sent during the time period and not messages that were only updated during this period. For example, if a message was updated yesterday but sent two days ago, you would need to set the created_at_after date before the message was sent to include that in your retrieval job. + + >📘 Date ranges are inclusive + > + > Requesting data for 2018-06-01 until 2018-06-30 will get all data for those days including those specified - e.g. 2018-06-01 00:00:00 until 2018-06-30 23:59:99. + + Parameters + ---------- + created_at_after : int + The start date that you request data for. It must be formatted as a unix timestamp. + + created_at_before : int + The end date that you request data for. It must be formatted as a unix timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DataExport] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "export/content/data", + method="POST", + json={ + "created_at_after": created_at_after, + "created_at_before": created_at_before, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataExport, + construct_type( + type_=DataExport, # 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 find( + self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DataExport]: + """ + You can view the status of your job by sending a `GET` request to the URL + `https://api.intercom.io/export/content/data/{job_identifier}` - the `{job_identifier}` is the value returned in the response when you first created the export job. More on it can be seen in the Export Job Model. + + > 🚧 Jobs expire after two days + > All jobs that have completed processing (and are thus available to download from the provided URL) will have an expiry limit of two days from when the export ob completed. After this, the data will no longer be available. + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DataExport] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"export/content/data/{jsonable_encoder(job_identifier)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataExport, + construct_type( + type_=DataExport, # 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 cancel( + self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DataExport]: + """ + You can cancel your job + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DataExport] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"export/cancel/{jsonable_encoder(job_identifier)}", + method="POST", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataExport, + construct_type( + type_=DataExport, # 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 download( + self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[None]: + """ + When a job has a status of complete, and thus a filled download_url, you can download your data by hitting that provided URL, formatted like so: https://api.intercom.io/download/content/data/xyz1234. + + Your exported message data will be streamed continuously back down to you in a gzipped CSV format. + + > 📘 Octet header required + > + > You will have to specify the header Accept: `application/octet-stream` when hitting this endpoint. + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + f"download/content/data/{jsonable_encoder(job_identifier)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + _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 AsyncRawDataExportClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def export_reporting_data( + self, + job_identifier: str, + *, + app_id: str, + client_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DataExportExportReportingDataResponse]: + """ + Parameters + ---------- + job_identifier : str + Unique identifier of the job. + + app_id : str + The Intercom defined code of the workspace the company is associated to. + + client_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DataExportExportReportingDataResponse] + Job status returned successfully + """ + _response = await self._client_wrapper.httpx_client.request( + f"export/reporting_data/{jsonable_encoder(job_identifier)}", + method="GET", + params={ + "app_id": app_id, + "client_id": client_id, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataExportExportReportingDataResponse, + construct_type( + type_=DataExportExportReportingDataResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + 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 download_reporting_data_export( + self, job_identifier: str, *, app_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + Download the data from a completed reporting data export job. + + > Octet header required + > + > You will have to specify the header Accept: `application/octet-stream` when hitting this endpoint. + + Parameters + ---------- + job_identifier : str + + app_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + f"download/reporting_data/{jsonable_encoder(job_identifier)}", + method="GET", + params={ + "app_id": app_id, + }, + headers={ + "Accept": "application/octet-stream", + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + 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 create( + self, *, created_at_after: int, created_at_before: int, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DataExport]: + """ + To create your export job, you need to send a `POST` request to the export endpoint `https://api.intercom.io/export/content/data`. + + The only parameters you need to provide are the range of dates that you want exported. + + >🚧 Limit of one active job + > + > You can only have one active job per workspace. You will receive a HTTP status code of 429 with the message Exceeded rate limit of 1 pending message data export jobs if you attempt to create a second concurrent job. + + >❗️ Updated_at not included + > + > It should be noted that the timeframe only includes messages sent during the time period and not messages that were only updated during this period. For example, if a message was updated yesterday but sent two days ago, you would need to set the created_at_after date before the message was sent to include that in your retrieval job. + + >📘 Date ranges are inclusive + > + > Requesting data for 2018-06-01 until 2018-06-30 will get all data for those days including those specified - e.g. 2018-06-01 00:00:00 until 2018-06-30 23:59:99. + + Parameters + ---------- + created_at_after : int + The start date that you request data for. It must be formatted as a unix timestamp. + + created_at_before : int + The end date that you request data for. It must be formatted as a unix timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DataExport] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "export/content/data", + method="POST", + json={ + "created_at_after": created_at_after, + "created_at_before": created_at_before, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataExport, + construct_type( + type_=DataExport, # 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 find( + self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DataExport]: + """ + You can view the status of your job by sending a `GET` request to the URL + `https://api.intercom.io/export/content/data/{job_identifier}` - the `{job_identifier}` is the value returned in the response when you first created the export job. More on it can be seen in the Export Job Model. + + > 🚧 Jobs expire after two days + > All jobs that have completed processing (and are thus available to download from the provided URL) will have an expiry limit of two days from when the export ob completed. After this, the data will no longer be available. + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DataExport] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"export/content/data/{jsonable_encoder(job_identifier)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataExport, + construct_type( + type_=DataExport, # 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 cancel( + self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DataExport]: + """ + You can cancel your job + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DataExport] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"export/cancel/{jsonable_encoder(job_identifier)}", + method="POST", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataExport, + construct_type( + type_=DataExport, # 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 download( + self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + When a job has a status of complete, and thus a filled download_url, you can download your data by hitting that provided URL, formatted like so: https://api.intercom.io/download/content/data/xyz1234. + + Your exported message data will be streamed continuously back down to you in a gzipped CSV format. + + > 📘 Octet header required + > + > You will have to specify the header Accept: `application/octet-stream` when hitting this endpoint. + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + f"download/content/data/{jsonable_encoder(job_identifier)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + _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) diff --git a/src/intercom/data_export/types/__init__.py b/src/intercom/data_export/types/__init__.py new file mode 100644 index 00000000..382da482 --- /dev/null +++ b/src/intercom/data_export/types/__init__.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .data_export import DataExport + from .data_export_export_reporting_data_response import DataExportExportReportingDataResponse + from .data_export_status import DataExportStatus +_dynamic_imports: typing.Dict[str, str] = { + "DataExport": ".data_export", + "DataExportExportReportingDataResponse": ".data_export_export_reporting_data_response", + "DataExportStatus": ".data_export_status", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["DataExport", "DataExportExportReportingDataResponse", "DataExportStatus"] diff --git a/src/intercom/data_export/types/data_export.py b/src/intercom/data_export/types/data_export.py new file mode 100644 index 00000000..0e053aa3 --- /dev/null +++ b/src/intercom/data_export/types/data_export.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .data_export_status import DataExportStatus + + +class DataExport(UncheckedBaseModel): + """ + The data export api is used to view all message sent & viewed in a given timeframe. + """ + + job_identifier: typing.Optional[str] = pydantic.Field(default=None) + """ + The identifier for your job. + """ + + status: typing.Optional[DataExportStatus] = pydantic.Field(default=None) + """ + The current state of your job. + """ + + download_expires_at: typing.Optional[str] = pydantic.Field(default=None) + """ + The time after which you will not be able to access the data. + """ + + download_url: typing.Optional[str] = pydantic.Field(default=None) + """ + The location where you can download your data. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/data_export/types/data_export_export_reporting_data_response.py b/src/intercom/data_export/types/data_export_export_reporting_data_response.py new file mode 100644 index 00000000..0a245c66 --- /dev/null +++ b/src/intercom/data_export/types/data_export_export_reporting_data_response.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class DataExportExportReportingDataResponse(UncheckedBaseModel): + job_identifier: typing.Optional[str] = None + status: typing.Optional[str] = None + download_url: typing.Optional[str] = None + download_expires_at: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/data_export/types/data_export_status.py b/src/intercom/data_export/types/data_export_status.py new file mode 100644 index 00000000..04cd48c9 --- /dev/null +++ b/src/intercom/data_export/types/data_export_status.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +DataExportStatus = typing.Union[ + typing.Literal["pending", "in_progress", "failed", "completed", "no_data", "canceled"], typing.Any +] diff --git a/src/intercom/environment.py b/src/intercom/environment.py new file mode 100644 index 00000000..270ead4b --- /dev/null +++ b/src/intercom/environment.py @@ -0,0 +1,9 @@ +# This file was auto-generated by Fern from our API Definition. + +import enum + + +class IntercomEnvironment(enum.Enum): + US_PRODUCTION = "https://api.intercom.io" + EU_PRODUCTION = "https://api.eu.intercom.io" + AU_PRODUCTION = "https://api.au.intercom.io" diff --git a/src/intercom/errors/__init__.py b/src/intercom/errors/__init__.py new file mode 100644 index 00000000..2cf3ca1a --- /dev/null +++ b/src/intercom/errors/__init__.py @@ -0,0 +1,53 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .bad_request_error import BadRequestError + from .forbidden_error import ForbiddenError + from .not_found_error import NotFoundError + from .too_many_requests_error import TooManyRequestsError + from .unauthorized_error import UnauthorizedError + from .unprocessable_entity_error import UnprocessableEntityError +_dynamic_imports: typing.Dict[str, str] = { + "BadRequestError": ".bad_request_error", + "ForbiddenError": ".forbidden_error", + "NotFoundError": ".not_found_error", + "TooManyRequestsError": ".too_many_requests_error", + "UnauthorizedError": ".unauthorized_error", + "UnprocessableEntityError": ".unprocessable_entity_error", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "BadRequestError", + "ForbiddenError", + "NotFoundError", + "TooManyRequestsError", + "UnauthorizedError", + "UnprocessableEntityError", +] diff --git a/src/intercom/errors/bad_request_error.py b/src/intercom/errors/bad_request_error.py new file mode 100644 index 00000000..ec78e269 --- /dev/null +++ b/src/intercom/errors/bad_request_error.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.api_error import ApiError + + +class BadRequestError(ApiError): + def __init__(self, body: typing.Any, headers: typing.Optional[typing.Dict[str, str]] = None): + super().__init__(status_code=400, headers=headers, body=body) diff --git a/src/intercom/errors/forbidden_error.py b/src/intercom/errors/forbidden_error.py new file mode 100644 index 00000000..e501c57c --- /dev/null +++ b/src/intercom/errors/forbidden_error.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.api_error import ApiError +from ..types.error import Error + + +class ForbiddenError(ApiError): + def __init__(self, body: Error, headers: typing.Optional[typing.Dict[str, str]] = None): + super().__init__(status_code=403, headers=headers, body=body) diff --git a/src/intercom/errors/not_found_error.py b/src/intercom/errors/not_found_error.py new file mode 100644 index 00000000..75f557df --- /dev/null +++ b/src/intercom/errors/not_found_error.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.api_error import ApiError + + +class NotFoundError(ApiError): + def __init__(self, body: typing.Any, headers: typing.Optional[typing.Dict[str, str]] = None): + super().__init__(status_code=404, headers=headers, body=body) diff --git a/src/intercom/errors/too_many_requests_error.py b/src/intercom/errors/too_many_requests_error.py new file mode 100644 index 00000000..a0743ee2 --- /dev/null +++ b/src/intercom/errors/too_many_requests_error.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.api_error import ApiError +from ..types.error import Error + + +class TooManyRequestsError(ApiError): + def __init__(self, body: Error, headers: typing.Optional[typing.Dict[str, str]] = None): + super().__init__(status_code=429, headers=headers, body=body) diff --git a/src/intercom/errors/unauthorized_error.py b/src/intercom/errors/unauthorized_error.py new file mode 100644 index 00000000..2345489b --- /dev/null +++ b/src/intercom/errors/unauthorized_error.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.api_error import ApiError +from ..types.error import Error + + +class UnauthorizedError(ApiError): + def __init__(self, body: Error, headers: typing.Optional[typing.Dict[str, str]] = None): + super().__init__(status_code=401, headers=headers, body=body) diff --git a/src/intercom/errors/unprocessable_entity_error.py b/src/intercom/errors/unprocessable_entity_error.py new file mode 100644 index 00000000..1c801a4b --- /dev/null +++ b/src/intercom/errors/unprocessable_entity_error.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.api_error import ApiError + + +class UnprocessableEntityError(ApiError): + def __init__(self, body: typing.Any, headers: typing.Optional[typing.Dict[str, str]] = None): + super().__init__(status_code=422, headers=headers, body=body) diff --git a/src/intercom/events/__init__.py b/src/intercom/events/__init__.py new file mode 100644 index 00000000..59e29fcf --- /dev/null +++ b/src/intercom/events/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import CreateDataEventSummariesRequestEventSummaries +_dynamic_imports: typing.Dict[str, str] = {"CreateDataEventSummariesRequestEventSummaries": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["CreateDataEventSummariesRequestEventSummaries"] diff --git a/src/intercom/events/client.py b/src/intercom/events/client.py new file mode 100644 index 00000000..0e383d94 --- /dev/null +++ b/src/intercom/events/client.py @@ -0,0 +1,461 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.create_data_event_request import CreateDataEventRequest +from ..types.data_event_summary import DataEventSummary +from .raw_client import AsyncRawEventsClient, RawEventsClient +from .types.create_data_event_summaries_request_event_summaries import CreateDataEventSummariesRequestEventSummaries + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class EventsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawEventsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawEventsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawEventsClient + """ + return self._raw_client + + def list( + self, + *, + type: str, + user_id: typing.Optional[str] = None, + intercom_user_id: typing.Optional[str] = None, + email: typing.Optional[str] = None, + summary: typing.Optional[bool] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> DataEventSummary: + """ + + > 🚧 + > + > Please note that you can only 'list' events that are less than 90 days old. Event counts and summaries will still include your events older than 90 days but you cannot 'list' these events individually if they are older than 90 days + + The events belonging to a customer can be listed by sending a GET request to `https://api.intercom.io/events` with a user or lead identifier along with a `type` parameter. The identifier parameter can be one of `user_id`, `email` or `intercom_user_id`. The `type` parameter value must be `user`. + + - `https://api.intercom.io/events?type=user&user_id={user_id}` + - `https://api.intercom.io/events?type=user&email={email}` + - `https://api.intercom.io/events?type=user&intercom_user_id={id}` (this call can be used to list leads) + + The `email` parameter value should be [url encoded](http://en.wikipedia.org/wiki/Percent-encoding) when sending. + + You can optionally define the result page size as well with the `per_page` parameter. + + Parameters + ---------- + type : str + The value must be user + + user_id : typing.Optional[str] + user_id query parameter + + intercom_user_id : typing.Optional[str] + intercom_user_id query parameter + + email : typing.Optional[str] + email query parameter + + summary : typing.Optional[bool] + summary flag + + per_page : typing.Optional[int] + How many results to display per page. Defaults to 15 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataEventSummary + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.events.list( + user_id="user_id", + intercom_user_id="intercom_user_id", + email="email", + type="type", + summary=True, + per_page=1, + ) + """ + _response = self._raw_client.list( + type=type, + user_id=user_id, + intercom_user_id=intercom_user_id, + email=email, + summary=summary, + per_page=per_page, + request_options=request_options, + ) + return _response.data + + def create( + self, *, request: CreateDataEventRequest, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + + You will need an Access Token that has write permissions to send Events. Once you have a key you can submit events via POST to the Events resource, which is located at https://api.intercom.io/events, or you can send events using one of the client libraries. When working with the HTTP API directly a client should send the event with a `Content-Type` of `application/json`. + + When using the JavaScript API, [adding the code to your app](http://docs.intercom.io/configuring-Intercom/tracking-user-events-in-your-app) makes the Events API available. Once added, you can submit an event using the `trackEvent` method. This will associate the event with the Lead or currently logged-in user or logged-out visitor/lead and send it to Intercom. The final parameter is a map that can be used to send optional metadata about the event. + + With the Ruby client you pass a hash describing the event to `Intercom::Event.create`, or call the `track_user` method directly on the current user object (e.g. `user.track_event`). + + **NB: For the JSON object types, please note that we do not currently support nested JSON structure.** + + | Type | Description | Example | + | :-------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------- | + | String | The value is a JSON String | `"source":"desktop"` | + | Number | The value is a JSON Number | `"load": 3.67` | + | Date | The key ends with the String `_date` and the value is a [Unix timestamp](http://en.wikipedia.org/wiki/Unix_time), assumed to be in the [UTC](http://en.wikipedia.org/wiki/Coordinated_Universal_Time) timezone. | `"contact_date": 1392036272` | + | Link | The value is a HTTP or HTTPS URI. | `"article": "https://example.org/ab1de.html"` | + | Rich Link | The value is a JSON object that contains `url` and `value` keys. | `"article": {"url": "https://example.org/ab1de.html", "value":"the dude abides"}` | + | Monetary Amount | The value is a JSON object that contains `amount` and `currency` keys. The `amount` key is a positive integer representing the amount in cents. The price in the example to the right denotes €349.99. | `"price": {"amount": 34999, "currency": "eur"}` | + + **Lead Events** + + When submitting events for Leads, you will need to specify the Lead's `id`. + + **Metadata behaviour** + + - We currently limit the number of tracked metadata keys to 10 per event. Once the quota is reached, we ignore any further keys we receive. The first 10 metadata keys are determined by the order in which they are sent in with the event. + - It is not possible to change the metadata keys once the event has been sent. A new event will need to be created with the new keys and you can archive the old one. + - There might be up to 24 hrs delay when you send a new metadata for an existing event. + + **Event de-duplication** + + The API may detect and ignore duplicate events. Each event is uniquely identified as a combination of the following data - the Workspace identifier, the Contact external identifier, the Data Event name and the Data Event created time. As a result, it is **strongly recommended** to send a second granularity Unix timestamp in the `created_at` field. + + Duplicated events are responded to using the normal `202 Accepted` code - an error is not thrown, however repeat requests will be counted against any rate limit that is in place. + + ### HTTP API Responses + + - Successful responses to submitted events return `202 Accepted` with an empty body. + - Unauthorised access will be rejected with a `401 Unauthorized` or `403 Forbidden` response code. + - Events sent about users that cannot be found will return a `404 Not Found`. + - Event lists containing duplicate events will have those duplicates ignored. + - Server errors will return a `500` response code and may contain an error message in the body. + + Parameters + ---------- + request : CreateDataEventRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from intercom import CreateDataEventRequestWithId, Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.events.create( + request=CreateDataEventRequestWithId( + id="8a88a590-e1c3-41e2-a502-e0649dbf721c", + event_name="invited-friend", + created_at=1671028894, + ), + ) + """ + _response = self._raw_client.create(request=request, request_options=request_options) + return _response.data + + def summaries( + self, + *, + user_id: typing.Optional[str] = OMIT, + event_summaries: typing.Optional[CreateDataEventSummariesRequestEventSummaries] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> None: + """ + Create event summaries for a user. Event summaries are used to track the number of times an event has occurred, the first time it occurred and the last time it occurred. + + Parameters + ---------- + user_id : typing.Optional[str] + Your identifier for the user. + + event_summaries : typing.Optional[CreateDataEventSummariesRequestEventSummaries] + A list of event summaries for the user. Each event summary should contain the event name, the time the event occurred, and the number of times the event occurred. The event name should be a past tense 'verb-noun' combination, to improve readability, for example `updated-plan`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.events.summaries() + """ + _response = self._raw_client.summaries( + user_id=user_id, event_summaries=event_summaries, request_options=request_options + ) + return _response.data + + +class AsyncEventsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawEventsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawEventsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawEventsClient + """ + return self._raw_client + + async def list( + self, + *, + type: str, + user_id: typing.Optional[str] = None, + intercom_user_id: typing.Optional[str] = None, + email: typing.Optional[str] = None, + summary: typing.Optional[bool] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> DataEventSummary: + """ + + > 🚧 + > + > Please note that you can only 'list' events that are less than 90 days old. Event counts and summaries will still include your events older than 90 days but you cannot 'list' these events individually if they are older than 90 days + + The events belonging to a customer can be listed by sending a GET request to `https://api.intercom.io/events` with a user or lead identifier along with a `type` parameter. The identifier parameter can be one of `user_id`, `email` or `intercom_user_id`. The `type` parameter value must be `user`. + + - `https://api.intercom.io/events?type=user&user_id={user_id}` + - `https://api.intercom.io/events?type=user&email={email}` + - `https://api.intercom.io/events?type=user&intercom_user_id={id}` (this call can be used to list leads) + + The `email` parameter value should be [url encoded](http://en.wikipedia.org/wiki/Percent-encoding) when sending. + + You can optionally define the result page size as well with the `per_page` parameter. + + Parameters + ---------- + type : str + The value must be user + + user_id : typing.Optional[str] + user_id query parameter + + intercom_user_id : typing.Optional[str] + intercom_user_id query parameter + + email : typing.Optional[str] + email query parameter + + summary : typing.Optional[bool] + summary flag + + per_page : typing.Optional[int] + How many results to display per page. Defaults to 15 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataEventSummary + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.events.list( + user_id="user_id", + intercom_user_id="intercom_user_id", + email="email", + type="type", + summary=True, + per_page=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list( + type=type, + user_id=user_id, + intercom_user_id=intercom_user_id, + email=email, + summary=summary, + per_page=per_page, + request_options=request_options, + ) + return _response.data + + async def create( + self, *, request: CreateDataEventRequest, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + + You will need an Access Token that has write permissions to send Events. Once you have a key you can submit events via POST to the Events resource, which is located at https://api.intercom.io/events, or you can send events using one of the client libraries. When working with the HTTP API directly a client should send the event with a `Content-Type` of `application/json`. + + When using the JavaScript API, [adding the code to your app](http://docs.intercom.io/configuring-Intercom/tracking-user-events-in-your-app) makes the Events API available. Once added, you can submit an event using the `trackEvent` method. This will associate the event with the Lead or currently logged-in user or logged-out visitor/lead and send it to Intercom. The final parameter is a map that can be used to send optional metadata about the event. + + With the Ruby client you pass a hash describing the event to `Intercom::Event.create`, or call the `track_user` method directly on the current user object (e.g. `user.track_event`). + + **NB: For the JSON object types, please note that we do not currently support nested JSON structure.** + + | Type | Description | Example | + | :-------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------- | + | String | The value is a JSON String | `"source":"desktop"` | + | Number | The value is a JSON Number | `"load": 3.67` | + | Date | The key ends with the String `_date` and the value is a [Unix timestamp](http://en.wikipedia.org/wiki/Unix_time), assumed to be in the [UTC](http://en.wikipedia.org/wiki/Coordinated_Universal_Time) timezone. | `"contact_date": 1392036272` | + | Link | The value is a HTTP or HTTPS URI. | `"article": "https://example.org/ab1de.html"` | + | Rich Link | The value is a JSON object that contains `url` and `value` keys. | `"article": {"url": "https://example.org/ab1de.html", "value":"the dude abides"}` | + | Monetary Amount | The value is a JSON object that contains `amount` and `currency` keys. The `amount` key is a positive integer representing the amount in cents. The price in the example to the right denotes €349.99. | `"price": {"amount": 34999, "currency": "eur"}` | + + **Lead Events** + + When submitting events for Leads, you will need to specify the Lead's `id`. + + **Metadata behaviour** + + - We currently limit the number of tracked metadata keys to 10 per event. Once the quota is reached, we ignore any further keys we receive. The first 10 metadata keys are determined by the order in which they are sent in with the event. + - It is not possible to change the metadata keys once the event has been sent. A new event will need to be created with the new keys and you can archive the old one. + - There might be up to 24 hrs delay when you send a new metadata for an existing event. + + **Event de-duplication** + + The API may detect and ignore duplicate events. Each event is uniquely identified as a combination of the following data - the Workspace identifier, the Contact external identifier, the Data Event name and the Data Event created time. As a result, it is **strongly recommended** to send a second granularity Unix timestamp in the `created_at` field. + + Duplicated events are responded to using the normal `202 Accepted` code - an error is not thrown, however repeat requests will be counted against any rate limit that is in place. + + ### HTTP API Responses + + - Successful responses to submitted events return `202 Accepted` with an empty body. + - Unauthorised access will be rejected with a `401 Unauthorized` or `403 Forbidden` response code. + - Events sent about users that cannot be found will return a `404 Not Found`. + - Event lists containing duplicate events will have those duplicates ignored. + - Server errors will return a `500` response code and may contain an error message in the body. + + Parameters + ---------- + request : CreateDataEventRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom, CreateDataEventRequestWithId + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.events.create( + request=CreateDataEventRequestWithId( + id="8a88a590-e1c3-41e2-a502-e0649dbf721c", + event_name="invited-friend", + created_at=1671028894, + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create(request=request, request_options=request_options) + return _response.data + + async def summaries( + self, + *, + user_id: typing.Optional[str] = OMIT, + event_summaries: typing.Optional[CreateDataEventSummariesRequestEventSummaries] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> None: + """ + Create event summaries for a user. Event summaries are used to track the number of times an event has occurred, the first time it occurred and the last time it occurred. + + Parameters + ---------- + user_id : typing.Optional[str] + Your identifier for the user. + + event_summaries : typing.Optional[CreateDataEventSummariesRequestEventSummaries] + A list of event summaries for the user. Each event summary should contain the event name, the time the event occurred, and the number of times the event occurred. The event name should be a past tense 'verb-noun' combination, to improve readability, for example `updated-plan`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.events.summaries() + + + asyncio.run(main()) + """ + _response = await self._raw_client.summaries( + user_id=user_id, event_summaries=event_summaries, request_options=request_options + ) + return _response.data diff --git a/src/intercom/events/raw_client.py b/src/intercom/events/raw_client.py new file mode 100644 index 00000000..ffcbea75 --- /dev/null +++ b/src/intercom/events/raw_client.py @@ -0,0 +1,511 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.http_response import AsyncHttpResponse, HttpResponse +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.unauthorized_error import UnauthorizedError +from ..types.create_data_event_request import CreateDataEventRequest +from ..types.data_event_summary import DataEventSummary +from ..types.error import Error +from .types.create_data_event_summaries_request_event_summaries import CreateDataEventSummariesRequestEventSummaries + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawEventsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list( + self, + *, + type: str, + user_id: typing.Optional[str] = None, + intercom_user_id: typing.Optional[str] = None, + email: typing.Optional[str] = None, + summary: typing.Optional[bool] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DataEventSummary]: + """ + + > 🚧 + > + > Please note that you can only 'list' events that are less than 90 days old. Event counts and summaries will still include your events older than 90 days but you cannot 'list' these events individually if they are older than 90 days + + The events belonging to a customer can be listed by sending a GET request to `https://api.intercom.io/events` with a user or lead identifier along with a `type` parameter. The identifier parameter can be one of `user_id`, `email` or `intercom_user_id`. The `type` parameter value must be `user`. + + - `https://api.intercom.io/events?type=user&user_id={user_id}` + - `https://api.intercom.io/events?type=user&email={email}` + - `https://api.intercom.io/events?type=user&intercom_user_id={id}` (this call can be used to list leads) + + The `email` parameter value should be [url encoded](http://en.wikipedia.org/wiki/Percent-encoding) when sending. + + You can optionally define the result page size as well with the `per_page` parameter. + + Parameters + ---------- + type : str + The value must be user + + user_id : typing.Optional[str] + user_id query parameter + + intercom_user_id : typing.Optional[str] + intercom_user_id query parameter + + email : typing.Optional[str] + email query parameter + + summary : typing.Optional[bool] + summary flag + + per_page : typing.Optional[int] + How many results to display per page. Defaults to 15 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DataEventSummary] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "events", + method="GET", + params={ + "user_id": user_id, + "intercom_user_id": intercom_user_id, + "email": email, + "type": type, + "summary": summary, + "per_page": per_page, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataEventSummary, + construct_type( + type_=DataEventSummary, # 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 create( + self, *, request: CreateDataEventRequest, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[None]: + """ + + You will need an Access Token that has write permissions to send Events. Once you have a key you can submit events via POST to the Events resource, which is located at https://api.intercom.io/events, or you can send events using one of the client libraries. When working with the HTTP API directly a client should send the event with a `Content-Type` of `application/json`. + + When using the JavaScript API, [adding the code to your app](http://docs.intercom.io/configuring-Intercom/tracking-user-events-in-your-app) makes the Events API available. Once added, you can submit an event using the `trackEvent` method. This will associate the event with the Lead or currently logged-in user or logged-out visitor/lead and send it to Intercom. The final parameter is a map that can be used to send optional metadata about the event. + + With the Ruby client you pass a hash describing the event to `Intercom::Event.create`, or call the `track_user` method directly on the current user object (e.g. `user.track_event`). + + **NB: For the JSON object types, please note that we do not currently support nested JSON structure.** + + | Type | Description | Example | + | :-------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------- | + | String | The value is a JSON String | `"source":"desktop"` | + | Number | The value is a JSON Number | `"load": 3.67` | + | Date | The key ends with the String `_date` and the value is a [Unix timestamp](http://en.wikipedia.org/wiki/Unix_time), assumed to be in the [UTC](http://en.wikipedia.org/wiki/Coordinated_Universal_Time) timezone. | `"contact_date": 1392036272` | + | Link | The value is a HTTP or HTTPS URI. | `"article": "https://example.org/ab1de.html"` | + | Rich Link | The value is a JSON object that contains `url` and `value` keys. | `"article": {"url": "https://example.org/ab1de.html", "value":"the dude abides"}` | + | Monetary Amount | The value is a JSON object that contains `amount` and `currency` keys. The `amount` key is a positive integer representing the amount in cents. The price in the example to the right denotes €349.99. | `"price": {"amount": 34999, "currency": "eur"}` | + + **Lead Events** + + When submitting events for Leads, you will need to specify the Lead's `id`. + + **Metadata behaviour** + + - We currently limit the number of tracked metadata keys to 10 per event. Once the quota is reached, we ignore any further keys we receive. The first 10 metadata keys are determined by the order in which they are sent in with the event. + - It is not possible to change the metadata keys once the event has been sent. A new event will need to be created with the new keys and you can archive the old one. + - There might be up to 24 hrs delay when you send a new metadata for an existing event. + + **Event de-duplication** + + The API may detect and ignore duplicate events. Each event is uniquely identified as a combination of the following data - the Workspace identifier, the Contact external identifier, the Data Event name and the Data Event created time. As a result, it is **strongly recommended** to send a second granularity Unix timestamp in the `created_at` field. + + Duplicated events are responded to using the normal `202 Accepted` code - an error is not thrown, however repeat requests will be counted against any rate limit that is in place. + + ### HTTP API Responses + + - Successful responses to submitted events return `202 Accepted` with an empty body. + - Unauthorised access will be rejected with a `401 Unauthorized` or `403 Forbidden` response code. + - Events sent about users that cannot be found will return a `404 Not Found`. + - Event lists containing duplicate events will have those duplicates ignored. + - Server errors will return a `500` response code and may contain an error message in the body. + + Parameters + ---------- + request : CreateDataEventRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + "events", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateDataEventRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + 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 summaries( + self, + *, + user_id: typing.Optional[str] = OMIT, + event_summaries: typing.Optional[CreateDataEventSummariesRequestEventSummaries] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[None]: + """ + Create event summaries for a user. Event summaries are used to track the number of times an event has occurred, the first time it occurred and the last time it occurred. + + Parameters + ---------- + user_id : typing.Optional[str] + Your identifier for the user. + + event_summaries : typing.Optional[CreateDataEventSummariesRequestEventSummaries] + A list of event summaries for the user. Each event summary should contain the event name, the time the event occurred, and the number of times the event occurred. The event name should be a past tense 'verb-noun' combination, to improve readability, for example `updated-plan`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + "events/summaries", + method="POST", + json={ + "user_id": user_id, + "event_summaries": convert_and_respect_annotation_metadata( + object_=event_summaries, annotation=CreateDataEventSummariesRequestEventSummaries, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + 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) + + +class AsyncRawEventsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, + *, + type: str, + user_id: typing.Optional[str] = None, + intercom_user_id: typing.Optional[str] = None, + email: typing.Optional[str] = None, + summary: typing.Optional[bool] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DataEventSummary]: + """ + + > 🚧 + > + > Please note that you can only 'list' events that are less than 90 days old. Event counts and summaries will still include your events older than 90 days but you cannot 'list' these events individually if they are older than 90 days + + The events belonging to a customer can be listed by sending a GET request to `https://api.intercom.io/events` with a user or lead identifier along with a `type` parameter. The identifier parameter can be one of `user_id`, `email` or `intercom_user_id`. The `type` parameter value must be `user`. + + - `https://api.intercom.io/events?type=user&user_id={user_id}` + - `https://api.intercom.io/events?type=user&email={email}` + - `https://api.intercom.io/events?type=user&intercom_user_id={id}` (this call can be used to list leads) + + The `email` parameter value should be [url encoded](http://en.wikipedia.org/wiki/Percent-encoding) when sending. + + You can optionally define the result page size as well with the `per_page` parameter. + + Parameters + ---------- + type : str + The value must be user + + user_id : typing.Optional[str] + user_id query parameter + + intercom_user_id : typing.Optional[str] + intercom_user_id query parameter + + email : typing.Optional[str] + email query parameter + + summary : typing.Optional[bool] + summary flag + + per_page : typing.Optional[int] + How many results to display per page. Defaults to 15 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DataEventSummary] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "events", + method="GET", + params={ + "user_id": user_id, + "intercom_user_id": intercom_user_id, + "email": email, + "type": type, + "summary": summary, + "per_page": per_page, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataEventSummary, + construct_type( + type_=DataEventSummary, # 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 create( + self, *, request: CreateDataEventRequest, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + + You will need an Access Token that has write permissions to send Events. Once you have a key you can submit events via POST to the Events resource, which is located at https://api.intercom.io/events, or you can send events using one of the client libraries. When working with the HTTP API directly a client should send the event with a `Content-Type` of `application/json`. + + When using the JavaScript API, [adding the code to your app](http://docs.intercom.io/configuring-Intercom/tracking-user-events-in-your-app) makes the Events API available. Once added, you can submit an event using the `trackEvent` method. This will associate the event with the Lead or currently logged-in user or logged-out visitor/lead and send it to Intercom. The final parameter is a map that can be used to send optional metadata about the event. + + With the Ruby client you pass a hash describing the event to `Intercom::Event.create`, or call the `track_user` method directly on the current user object (e.g. `user.track_event`). + + **NB: For the JSON object types, please note that we do not currently support nested JSON structure.** + + | Type | Description | Example | + | :-------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------- | + | String | The value is a JSON String | `"source":"desktop"` | + | Number | The value is a JSON Number | `"load": 3.67` | + | Date | The key ends with the String `_date` and the value is a [Unix timestamp](http://en.wikipedia.org/wiki/Unix_time), assumed to be in the [UTC](http://en.wikipedia.org/wiki/Coordinated_Universal_Time) timezone. | `"contact_date": 1392036272` | + | Link | The value is a HTTP or HTTPS URI. | `"article": "https://example.org/ab1de.html"` | + | Rich Link | The value is a JSON object that contains `url` and `value` keys. | `"article": {"url": "https://example.org/ab1de.html", "value":"the dude abides"}` | + | Monetary Amount | The value is a JSON object that contains `amount` and `currency` keys. The `amount` key is a positive integer representing the amount in cents. The price in the example to the right denotes €349.99. | `"price": {"amount": 34999, "currency": "eur"}` | + + **Lead Events** + + When submitting events for Leads, you will need to specify the Lead's `id`. + + **Metadata behaviour** + + - We currently limit the number of tracked metadata keys to 10 per event. Once the quota is reached, we ignore any further keys we receive. The first 10 metadata keys are determined by the order in which they are sent in with the event. + - It is not possible to change the metadata keys once the event has been sent. A new event will need to be created with the new keys and you can archive the old one. + - There might be up to 24 hrs delay when you send a new metadata for an existing event. + + **Event de-duplication** + + The API may detect and ignore duplicate events. Each event is uniquely identified as a combination of the following data - the Workspace identifier, the Contact external identifier, the Data Event name and the Data Event created time. As a result, it is **strongly recommended** to send a second granularity Unix timestamp in the `created_at` field. + + Duplicated events are responded to using the normal `202 Accepted` code - an error is not thrown, however repeat requests will be counted against any rate limit that is in place. + + ### HTTP API Responses + + - Successful responses to submitted events return `202 Accepted` with an empty body. + - Unauthorised access will be rejected with a `401 Unauthorized` or `403 Forbidden` response code. + - Events sent about users that cannot be found will return a `404 Not Found`. + - Event lists containing duplicate events will have those duplicates ignored. + - Server errors will return a `500` response code and may contain an error message in the body. + + Parameters + ---------- + request : CreateDataEventRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + "events", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateDataEventRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + 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 summaries( + self, + *, + user_id: typing.Optional[str] = OMIT, + event_summaries: typing.Optional[CreateDataEventSummariesRequestEventSummaries] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[None]: + """ + Create event summaries for a user. Event summaries are used to track the number of times an event has occurred, the first time it occurred and the last time it occurred. + + Parameters + ---------- + user_id : typing.Optional[str] + Your identifier for the user. + + event_summaries : typing.Optional[CreateDataEventSummariesRequestEventSummaries] + A list of event summaries for the user. Each event summary should contain the event name, the time the event occurred, and the number of times the event occurred. The event name should be a past tense 'verb-noun' combination, to improve readability, for example `updated-plan`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + "events/summaries", + method="POST", + json={ + "user_id": user_id, + "event_summaries": convert_and_respect_annotation_metadata( + object_=event_summaries, annotation=CreateDataEventSummariesRequestEventSummaries, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + 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) diff --git a/src/intercom/events/types/__init__.py b/src/intercom/events/types/__init__.py new file mode 100644 index 00000000..3a6f2909 --- /dev/null +++ b/src/intercom/events/types/__init__.py @@ -0,0 +1,36 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .create_data_event_summaries_request_event_summaries import CreateDataEventSummariesRequestEventSummaries +_dynamic_imports: typing.Dict[str, str] = { + "CreateDataEventSummariesRequestEventSummaries": ".create_data_event_summaries_request_event_summaries" +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["CreateDataEventSummariesRequestEventSummaries"] diff --git a/src/intercom/events/types/create_data_event_summaries_request_event_summaries.py b/src/intercom/events/types/create_data_event_summaries_request_event_summaries.py new file mode 100644 index 00000000..3ef5a0b4 --- /dev/null +++ b/src/intercom/events/types/create_data_event_summaries_request_event_summaries.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CreateDataEventSummariesRequestEventSummaries(UncheckedBaseModel): + """ + A list of event summaries for the user. Each event summary should contain the event name, the time the event occurred, and the number of times the event occurred. The event name should be a past tense 'verb-noun' combination, to improve readability, for example `updated-plan`. + """ + + event_name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the event that occurred. A good event name is typically a past tense 'verb-noun' combination, to improve readability, for example `updated-plan`. + """ + + count: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of times the event occurred. + """ + + first: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time the event was sent + """ + + last: typing.Optional[int] = pydantic.Field(default=None) + """ + The last time the event was sent + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/export/__init__.py b/src/intercom/export/__init__.py new file mode 100644 index 00000000..f5e527fe --- /dev/null +++ b/src/intercom/export/__init__.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + GetExportReportingDataGetDatasetsResponse, + GetExportReportingDataGetDatasetsResponseDataItem, + GetExportReportingDataGetDatasetsResponseDataItemAttributesItem, + PostExportReportingDataEnqueueResponse, + ) +_dynamic_imports: typing.Dict[str, str] = { + "GetExportReportingDataGetDatasetsResponse": ".types", + "GetExportReportingDataGetDatasetsResponseDataItem": ".types", + "GetExportReportingDataGetDatasetsResponseDataItemAttributesItem": ".types", + "PostExportReportingDataEnqueueResponse": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "GetExportReportingDataGetDatasetsResponse", + "GetExportReportingDataGetDatasetsResponseDataItem", + "GetExportReportingDataGetDatasetsResponseDataItemAttributesItem", + "PostExportReportingDataEnqueueResponse", +] diff --git a/src/intercom/export/client.py b/src/intercom/export/client.py new file mode 100644 index 00000000..ffec252a --- /dev/null +++ b/src/intercom/export/client.py @@ -0,0 +1,214 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawExportClient, RawExportClient +from .types.get_export_reporting_data_get_datasets_response import GetExportReportingDataGetDatasetsResponse +from .types.post_export_reporting_data_enqueue_response import PostExportReportingDataEnqueueResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class ExportClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawExportClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawExportClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawExportClient + """ + return self._raw_client + + def enqueue_a_new_reporting_data_export_job( + self, + *, + dataset_id: str, + attribute_ids: typing.Sequence[str], + start_time: int, + end_time: int, + request_options: typing.Optional[RequestOptions] = None, + ) -> PostExportReportingDataEnqueueResponse: + """ + Parameters + ---------- + dataset_id : str + + attribute_ids : typing.Sequence[str] + + start_time : int + + end_time : int + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PostExportReportingDataEnqueueResponse + Job enqueued successfully + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.export.enqueue_a_new_reporting_data_export_job( + dataset_id="conversation", + attribute_ids=["conversation_id", "conversation_started_at"], + start_time=1717490000, + end_time=1717510000, + ) + """ + _response = self._raw_client.enqueue_a_new_reporting_data_export_job( + dataset_id=dataset_id, + attribute_ids=attribute_ids, + start_time=start_time, + end_time=end_time, + request_options=request_options, + ) + return _response.data + + def list_available_datasets_and_attributes( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> GetExportReportingDataGetDatasetsResponse: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + GetExportReportingDataGetDatasetsResponse + List of datasets + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.export.list_available_datasets_and_attributes() + """ + _response = self._raw_client.list_available_datasets_and_attributes(request_options=request_options) + return _response.data + + +class AsyncExportClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawExportClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawExportClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawExportClient + """ + return self._raw_client + + async def enqueue_a_new_reporting_data_export_job( + self, + *, + dataset_id: str, + attribute_ids: typing.Sequence[str], + start_time: int, + end_time: int, + request_options: typing.Optional[RequestOptions] = None, + ) -> PostExportReportingDataEnqueueResponse: + """ + Parameters + ---------- + dataset_id : str + + attribute_ids : typing.Sequence[str] + + start_time : int + + end_time : int + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PostExportReportingDataEnqueueResponse + Job enqueued successfully + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.export.enqueue_a_new_reporting_data_export_job( + dataset_id="conversation", + attribute_ids=["conversation_id", "conversation_started_at"], + start_time=1717490000, + end_time=1717510000, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.enqueue_a_new_reporting_data_export_job( + dataset_id=dataset_id, + attribute_ids=attribute_ids, + start_time=start_time, + end_time=end_time, + request_options=request_options, + ) + return _response.data + + async def list_available_datasets_and_attributes( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> GetExportReportingDataGetDatasetsResponse: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + GetExportReportingDataGetDatasetsResponse + List of datasets + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.export.list_available_datasets_and_attributes() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_available_datasets_and_attributes(request_options=request_options) + return _response.data diff --git a/src/intercom/export/raw_client.py b/src/intercom/export/raw_client.py new file mode 100644 index 00000000..885784e1 --- /dev/null +++ b/src/intercom/export/raw_client.py @@ -0,0 +1,279 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.request_options import RequestOptions +from ..core.unchecked_base_model import construct_type +from ..errors.bad_request_error import BadRequestError +from ..errors.too_many_requests_error import TooManyRequestsError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from .types.get_export_reporting_data_get_datasets_response import GetExportReportingDataGetDatasetsResponse +from .types.post_export_reporting_data_enqueue_response import PostExportReportingDataEnqueueResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawExportClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def enqueue_a_new_reporting_data_export_job( + self, + *, + dataset_id: str, + attribute_ids: typing.Sequence[str], + start_time: int, + end_time: int, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[PostExportReportingDataEnqueueResponse]: + """ + Parameters + ---------- + dataset_id : str + + attribute_ids : typing.Sequence[str] + + start_time : int + + end_time : int + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[PostExportReportingDataEnqueueResponse] + Job enqueued successfully + """ + _response = self._client_wrapper.httpx_client.request( + "export/reporting_data/enqueue", + method="POST", + json={ + "dataset_id": dataset_id, + "attribute_ids": attribute_ids, + "start_time": start_time, + "end_time": end_time, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PostExportReportingDataEnqueueResponse, + construct_type( + type_=PostExportReportingDataEnqueueResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 == 429: + raise TooManyRequestsError( + 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_available_datasets_and_attributes( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[GetExportReportingDataGetDatasetsResponse]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[GetExportReportingDataGetDatasetsResponse] + List of datasets + """ + _response = self._client_wrapper.httpx_client.request( + "export/reporting_data/get_datasets", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + GetExportReportingDataGetDatasetsResponse, + construct_type( + type_=GetExportReportingDataGetDatasetsResponse, # 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 AsyncRawExportClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def enqueue_a_new_reporting_data_export_job( + self, + *, + dataset_id: str, + attribute_ids: typing.Sequence[str], + start_time: int, + end_time: int, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[PostExportReportingDataEnqueueResponse]: + """ + Parameters + ---------- + dataset_id : str + + attribute_ids : typing.Sequence[str] + + start_time : int + + end_time : int + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[PostExportReportingDataEnqueueResponse] + Job enqueued successfully + """ + _response = await self._client_wrapper.httpx_client.request( + "export/reporting_data/enqueue", + method="POST", + json={ + "dataset_id": dataset_id, + "attribute_ids": attribute_ids, + "start_time": start_time, + "end_time": end_time, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PostExportReportingDataEnqueueResponse, + construct_type( + type_=PostExportReportingDataEnqueueResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 == 429: + raise TooManyRequestsError( + 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_available_datasets_and_attributes( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[GetExportReportingDataGetDatasetsResponse]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[GetExportReportingDataGetDatasetsResponse] + List of datasets + """ + _response = await self._client_wrapper.httpx_client.request( + "export/reporting_data/get_datasets", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + GetExportReportingDataGetDatasetsResponse, + construct_type( + type_=GetExportReportingDataGetDatasetsResponse, # 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) diff --git a/src/intercom/export/types/__init__.py b/src/intercom/export/types/__init__.py new file mode 100644 index 00000000..44cfc00c --- /dev/null +++ b/src/intercom/export/types/__init__.py @@ -0,0 +1,51 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .get_export_reporting_data_get_datasets_response import GetExportReportingDataGetDatasetsResponse + from .get_export_reporting_data_get_datasets_response_data_item import ( + GetExportReportingDataGetDatasetsResponseDataItem, + ) + from .get_export_reporting_data_get_datasets_response_data_item_attributes_item import ( + GetExportReportingDataGetDatasetsResponseDataItemAttributesItem, + ) + from .post_export_reporting_data_enqueue_response import PostExportReportingDataEnqueueResponse +_dynamic_imports: typing.Dict[str, str] = { + "GetExportReportingDataGetDatasetsResponse": ".get_export_reporting_data_get_datasets_response", + "GetExportReportingDataGetDatasetsResponseDataItem": ".get_export_reporting_data_get_datasets_response_data_item", + "GetExportReportingDataGetDatasetsResponseDataItemAttributesItem": ".get_export_reporting_data_get_datasets_response_data_item_attributes_item", + "PostExportReportingDataEnqueueResponse": ".post_export_reporting_data_enqueue_response", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "GetExportReportingDataGetDatasetsResponse", + "GetExportReportingDataGetDatasetsResponseDataItem", + "GetExportReportingDataGetDatasetsResponseDataItemAttributesItem", + "PostExportReportingDataEnqueueResponse", +] diff --git a/src/intercom/export/types/get_export_reporting_data_get_datasets_response.py b/src/intercom/export/types/get_export_reporting_data_get_datasets_response.py new file mode 100644 index 00000000..b61cffc2 --- /dev/null +++ b/src/intercom/export/types/get_export_reporting_data_get_datasets_response.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .get_export_reporting_data_get_datasets_response_data_item import GetExportReportingDataGetDatasetsResponseDataItem + + +class GetExportReportingDataGetDatasetsResponse(UncheckedBaseModel): + type: typing.Optional[str] = None + data: typing.Optional[typing.List[GetExportReportingDataGetDatasetsResponseDataItem]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/export/types/get_export_reporting_data_get_datasets_response_data_item.py b/src/intercom/export/types/get_export_reporting_data_get_datasets_response_data_item.py new file mode 100644 index 00000000..206ba687 --- /dev/null +++ b/src/intercom/export/types/get_export_reporting_data_get_datasets_response_data_item.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .get_export_reporting_data_get_datasets_response_data_item_attributes_item import ( + GetExportReportingDataGetDatasetsResponseDataItemAttributesItem, +) + + +class GetExportReportingDataGetDatasetsResponseDataItem(UncheckedBaseModel): + id: typing.Optional[str] = None + name: typing.Optional[str] = None + description: typing.Optional[str] = None + default_time_attribute_id: typing.Optional[str] = None + attributes: typing.Optional[typing.List[GetExportReportingDataGetDatasetsResponseDataItemAttributesItem]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/export/types/get_export_reporting_data_get_datasets_response_data_item_attributes_item.py b/src/intercom/export/types/get_export_reporting_data_get_datasets_response_data_item_attributes_item.py new file mode 100644 index 00000000..593af9d8 --- /dev/null +++ b/src/intercom/export/types/get_export_reporting_data_get_datasets_response_data_item_attributes_item.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class GetExportReportingDataGetDatasetsResponseDataItemAttributesItem(UncheckedBaseModel): + id: typing.Optional[str] = None + name: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/export/types/post_export_reporting_data_enqueue_response.py b/src/intercom/export/types/post_export_reporting_data_enqueue_response.py new file mode 100644 index 00000000..5a6f7f19 --- /dev/null +++ b/src/intercom/export/types/post_export_reporting_data_enqueue_response.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class PostExportReportingDataEnqueueResponse(UncheckedBaseModel): + job_identifier: typing.Optional[str] = None + status: typing.Optional[str] = None + download_url: typing.Optional[str] = None + download_expires_at: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/help_center/__init__.py b/src/intercom/help_center/__init__.py new file mode 100644 index 00000000..4100fda2 --- /dev/null +++ b/src/intercom/help_center/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import Collection, HelpCenter, HelpCenterList +_dynamic_imports: typing.Dict[str, str] = {"Collection": ".types", "HelpCenter": ".types", "HelpCenterList": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Collection", "HelpCenter", "HelpCenterList"] diff --git a/src/intercom/help_center/types/__init__.py b/src/intercom/help_center/types/__init__.py new file mode 100644 index 00000000..57d6f641 --- /dev/null +++ b/src/intercom/help_center/types/__init__.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .collection import Collection + from .help_center import HelpCenter + from .help_center_list import HelpCenterList +_dynamic_imports: typing.Dict[str, str] = { + "Collection": ".collection", + "HelpCenter": ".help_center", + "HelpCenterList": ".help_center_list", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Collection", "HelpCenter", "HelpCenterList"] diff --git a/src/intercom/help_center/types/collection.py b/src/intercom/help_center/types/collection.py new file mode 100644 index 00000000..a7944fc3 --- /dev/null +++ b/src/intercom/help_center/types/collection.py @@ -0,0 +1,84 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.group_translated_content import GroupTranslatedContent + + +class Collection(UncheckedBaseModel): + """ + Collections are top level containers for Articles within the Help Center. + """ + + id: str = pydantic.Field() + """ + The unique identifier for the collection which is given by Intercom. + """ + + workspace_id: str = pydantic.Field() + """ + The id of the workspace which the collection belongs to. + """ + + name: str = pydantic.Field() + """ + The name of the collection. For multilingual collections, this will be the name of the default language's content. + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + The description of the collection. For multilingual help centers, this will be the description of the collection for the default language. + """ + + created_at: int = pydantic.Field() + """ + The time when the article was created (seconds). For multilingual articles, this will be the timestamp of creation of the default language's content. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the article was last updated (seconds). For multilingual articles, this will be the timestamp of last update of the default language's content. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL of the collection. For multilingual help centers, this will be the URL of the collection for the default language. + """ + + icon: typing.Optional[str] = pydantic.Field(default=None) + """ + The icon of the collection. + """ + + order: int = pydantic.Field() + """ + The order of the section in relation to others sections within a collection. Values go from `0` upwards. `0` is the default if there's no order. + """ + + default_locale: typing.Optional[str] = pydantic.Field(default=None) + """ + The default locale of the help center. This field is only returned for multilingual help centers. + """ + + translated_content: typing.Optional[GroupTranslatedContent] = None + parent_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the parent collection. If `null` then it is the first level collection. + """ + + help_center_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of the help center the collection is in. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/help_center/types/help_center.py b/src/intercom/help_center/types/help_center.py new file mode 100644 index 00000000..204d3b93 --- /dev/null +++ b/src/intercom/help_center/types/help_center.py @@ -0,0 +1,67 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class HelpCenter(UncheckedBaseModel): + """ + Help Centers contain collections + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the Help Center which is given by Intercom. + """ + + workspace_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the workspace which the Help Center belongs to. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the Help Center was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the Help Center was last updated. + """ + + identifier: typing.Optional[str] = pydantic.Field(default=None) + """ + The identifier of the Help Center. This is used in the URL of the Help Center. + """ + + website_turned_on: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the Help Center is turned on or not. This is controlled in your Help Center settings. + """ + + display_name: typing.Optional[str] = pydantic.Field(default=None) + """ + The display name of the Help Center only seen by teammates. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL for the help center, if you have a custom domain then this will show the URL using the custom domain. + """ + + custom_domain: typing.Optional[str] = pydantic.Field(default=None) + """ + Custom domain configured for the help center + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/help_center/types/help_center_list.py b/src/intercom/help_center/types/help_center_list.py new file mode 100644 index 00000000..2e746699 --- /dev/null +++ b/src/intercom/help_center/types/help_center_list.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .help_center import HelpCenter + + +class HelpCenterList(UncheckedBaseModel): + """ + A list of Help Centers belonging to the App + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object - `list`. + """ + + data: typing.Optional[typing.List[HelpCenter]] = pydantic.Field(default=None) + """ + An array of Help Center objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/help_centers/__init__.py b/src/intercom/help_centers/__init__.py new file mode 100644 index 00000000..2f2a8f52 --- /dev/null +++ b/src/intercom/help_centers/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from . import collections +_dynamic_imports: typing.Dict[str, str] = {"collections": ".collections"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["collections"] diff --git a/src/intercom/help_centers/client.py b/src/intercom/help_centers/client.py new file mode 100644 index 00000000..30cebeec --- /dev/null +++ b/src/intercom/help_centers/client.py @@ -0,0 +1,230 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.pagination import AsyncPager, SyncPager +from ..core.request_options import RequestOptions +from ..help_center.types.help_center import HelpCenter +from ..help_center.types.help_center_list import HelpCenterList +from .raw_client import AsyncRawHelpCentersClient, RawHelpCentersClient + +if typing.TYPE_CHECKING: + from .collections.client import AsyncCollectionsClient, CollectionsClient + + +class HelpCentersClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawHelpCentersClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._collections: typing.Optional[CollectionsClient] = None + + @property + def with_raw_response(self) -> RawHelpCentersClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawHelpCentersClient + """ + return self._raw_client + + def find(self, help_center_id: int, *, request_options: typing.Optional[RequestOptions] = None) -> HelpCenter: + """ + You can fetch the details of a single Help Center by making a GET request to `https://api.intercom.io/help_center/help_center/`. + + Parameters + ---------- + help_center_id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HelpCenter + Collection found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.help_centers.find( + help_center_id=1, + ) + """ + _response = self._raw_client.find(help_center_id, request_options=request_options) + return _response.data + + def list( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[HelpCenter, HelpCenterList]: + """ + You can list all Help Centers by making a GET request to `https://api.intercom.io/help_center/help_centers`. + + 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 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[HelpCenter, HelpCenterList] + Help Centers found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + response = client.help_centers.list() + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.list(page=page, per_page=per_page, request_options=request_options) + + @property + def collections(self): + if self._collections is None: + from .collections.client import CollectionsClient # noqa: E402 + + self._collections = CollectionsClient(client_wrapper=self._client_wrapper) + return self._collections + + +class AsyncHelpCentersClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawHelpCentersClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._collections: typing.Optional[AsyncCollectionsClient] = None + + @property + def with_raw_response(self) -> AsyncRawHelpCentersClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawHelpCentersClient + """ + return self._raw_client + + async def find(self, help_center_id: int, *, request_options: typing.Optional[RequestOptions] = None) -> HelpCenter: + """ + You can fetch the details of a single Help Center by making a GET request to `https://api.intercom.io/help_center/help_center/`. + + Parameters + ---------- + help_center_id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HelpCenter + Collection found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.help_centers.find( + help_center_id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.find(help_center_id, request_options=request_options) + return _response.data + + async def list( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[HelpCenter, HelpCenterList]: + """ + You can list all Help Centers by making a GET request to `https://api.intercom.io/help_center/help_centers`. + + 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 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[HelpCenter, HelpCenterList] + Help Centers found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + response = await client.help_centers.list() + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page + + + asyncio.run(main()) + """ + return await self._raw_client.list(page=page, per_page=per_page, request_options=request_options) + + @property + def collections(self): + if self._collections is None: + from .collections.client import AsyncCollectionsClient # noqa: E402 + + self._collections = AsyncCollectionsClient(client_wrapper=self._client_wrapper) + return self._collections diff --git a/src/intercom/help_centers/collections/__init__.py b/src/intercom/help_centers/collections/__init__.py new file mode 100644 index 00000000..5cde0202 --- /dev/null +++ b/src/intercom/help_centers/collections/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/src/intercom/help_centers/collections/client.py b/src/intercom/help_centers/collections/client.py new file mode 100644 index 00000000..e4616bdb --- /dev/null +++ b/src/intercom/help_centers/collections/client.py @@ -0,0 +1,538 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.pagination import AsyncPager, SyncPager +from ...core.request_options import RequestOptions +from ...help_center.types.collection import Collection +from ...types.collection_list import CollectionList +from ...types.deleted_collection_object import DeletedCollectionObject +from ...types.group_translated_content import GroupTranslatedContent +from .raw_client import AsyncRawCollectionsClient, RawCollectionsClient + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class CollectionsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawCollectionsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawCollectionsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawCollectionsClient + """ + return self._raw_client + + def list( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Collection, CollectionList]: + """ + You can fetch a list of all collections by making a GET request to `https://api.intercom.io/help_center/collections`. + + Collections will be returned in descending order on the `updated_at` attribute. This means if you need to iterate through results then we'll show the most recently updated collections first. + + 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 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[Collection, CollectionList] + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + response = client.help_centers.collections.list() + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.list(page=page, per_page=per_page, request_options=request_options) + + def create( + self, + *, + name: str, + description: typing.Optional[str] = OMIT, + translated_content: typing.Optional[GroupTranslatedContent] = OMIT, + parent_id: typing.Optional[str] = OMIT, + help_center_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Collection: + """ + You can create a new collection by making a POST request to `https://api.intercom.io/help_center/collections.` + + Parameters + ---------- + name : str + The name of the collection. For multilingual collections, this will be the name of the default language's content. + + description : typing.Optional[str] + The description of the collection. For multilingual collections, this will be the description of the default language's content. + + translated_content : typing.Optional[GroupTranslatedContent] + + parent_id : typing.Optional[str] + The id of the parent collection. If `null` then it will be created as the first level collection. + + help_center_id : typing.Optional[int] + The id of the help center where the collection will be created. If `null` then it will be created in the default help center. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Collection + collection created + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.help_centers.collections.create( + name="Thanks for everything", + ) + """ + _response = self._raw_client.create( + name=name, + description=description, + translated_content=translated_content, + parent_id=parent_id, + help_center_id=help_center_id, + request_options=request_options, + ) + return _response.data + + def find(self, collection_id: int, *, request_options: typing.Optional[RequestOptions] = None) -> Collection: + """ + You can fetch the details of a single collection by making a GET request to `https://api.intercom.io/help_center/collections/`. + + Parameters + ---------- + collection_id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Collection + Collection found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.help_centers.collections.find( + collection_id=1, + ) + """ + _response = self._raw_client.find(collection_id, request_options=request_options) + return _response.data + + def update( + self, + collection_id: int, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + translated_content: typing.Optional[GroupTranslatedContent] = OMIT, + parent_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Collection: + """ + You can update the details of a single collection by making a PUT request to `https://api.intercom.io/collections/`. + + Parameters + ---------- + collection_id : int + The unique identifier for the collection which is given by Intercom. + + name : typing.Optional[str] + The name of the collection. For multilingual collections, this will be the name of the default language's content. + + description : typing.Optional[str] + The description of the collection. For multilingual collections, this will be the description of the default language's content. + + translated_content : typing.Optional[GroupTranslatedContent] + + parent_id : typing.Optional[str] + The id of the parent collection. If `null` then it will be updated as the first level collection. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Collection + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.help_centers.collections.update( + collection_id=1, + name="Update collection name", + ) + """ + _response = self._raw_client.update( + collection_id, + name=name, + description=description, + translated_content=translated_content, + parent_id=parent_id, + request_options=request_options, + ) + return _response.data + + def delete( + self, collection_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeletedCollectionObject: + """ + You can delete a single collection by making a DELETE request to `https://api.intercom.io/collections/`. + + Parameters + ---------- + collection_id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeletedCollectionObject + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.help_centers.collections.delete( + collection_id=1, + ) + """ + _response = self._raw_client.delete(collection_id, request_options=request_options) + return _response.data + + +class AsyncCollectionsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawCollectionsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawCollectionsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawCollectionsClient + """ + return self._raw_client + + async def list( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[Collection, CollectionList]: + """ + You can fetch a list of all collections by making a GET request to `https://api.intercom.io/help_center/collections`. + + Collections will be returned in descending order on the `updated_at` attribute. This means if you need to iterate through results then we'll show the most recently updated collections first. + + 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 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[Collection, CollectionList] + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + response = await client.help_centers.collections.list() + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page + + + asyncio.run(main()) + """ + return await self._raw_client.list(page=page, per_page=per_page, request_options=request_options) + + async def create( + self, + *, + name: str, + description: typing.Optional[str] = OMIT, + translated_content: typing.Optional[GroupTranslatedContent] = OMIT, + parent_id: typing.Optional[str] = OMIT, + help_center_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Collection: + """ + You can create a new collection by making a POST request to `https://api.intercom.io/help_center/collections.` + + Parameters + ---------- + name : str + The name of the collection. For multilingual collections, this will be the name of the default language's content. + + description : typing.Optional[str] + The description of the collection. For multilingual collections, this will be the description of the default language's content. + + translated_content : typing.Optional[GroupTranslatedContent] + + parent_id : typing.Optional[str] + The id of the parent collection. If `null` then it will be created as the first level collection. + + help_center_id : typing.Optional[int] + The id of the help center where the collection will be created. If `null` then it will be created in the default help center. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Collection + collection created + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.help_centers.collections.create( + name="Thanks for everything", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + name=name, + description=description, + translated_content=translated_content, + parent_id=parent_id, + help_center_id=help_center_id, + request_options=request_options, + ) + return _response.data + + async def find(self, collection_id: int, *, request_options: typing.Optional[RequestOptions] = None) -> Collection: + """ + You can fetch the details of a single collection by making a GET request to `https://api.intercom.io/help_center/collections/`. + + Parameters + ---------- + collection_id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Collection + Collection found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.help_centers.collections.find( + collection_id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.find(collection_id, request_options=request_options) + return _response.data + + async def update( + self, + collection_id: int, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + translated_content: typing.Optional[GroupTranslatedContent] = OMIT, + parent_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Collection: + """ + You can update the details of a single collection by making a PUT request to `https://api.intercom.io/collections/`. + + Parameters + ---------- + collection_id : int + The unique identifier for the collection which is given by Intercom. + + name : typing.Optional[str] + The name of the collection. For multilingual collections, this will be the name of the default language's content. + + description : typing.Optional[str] + The description of the collection. For multilingual collections, this will be the description of the default language's content. + + translated_content : typing.Optional[GroupTranslatedContent] + + parent_id : typing.Optional[str] + The id of the parent collection. If `null` then it will be updated as the first level collection. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Collection + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.help_centers.collections.update( + collection_id=1, + name="Update collection name", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update( + collection_id, + name=name, + description=description, + translated_content=translated_content, + parent_id=parent_id, + request_options=request_options, + ) + return _response.data + + async def delete( + self, collection_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeletedCollectionObject: + """ + You can delete a single collection by making a DELETE request to `https://api.intercom.io/collections/`. + + Parameters + ---------- + collection_id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeletedCollectionObject + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.help_centers.collections.delete( + collection_id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete(collection_id, request_options=request_options) + return _response.data diff --git a/src/intercom/help_centers/collections/raw_client.py b/src/intercom/help_centers/collections/raw_client.py new file mode 100644 index 00000000..674fe4b0 --- /dev/null +++ b/src/intercom/help_centers/collections/raw_client.py @@ -0,0 +1,791 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.bad_request_error import BadRequestError +from ...errors.not_found_error import NotFoundError +from ...errors.unauthorized_error import UnauthorizedError +from ...help_center.types.collection import Collection +from ...types.collection_list import CollectionList +from ...types.deleted_collection_object import DeletedCollectionObject +from ...types.error import Error +from ...types.group_translated_content import GroupTranslatedContent + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawCollectionsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Collection, CollectionList]: + """ + You can fetch a list of all collections by making a GET request to `https://api.intercom.io/help_center/collections`. + + Collections will be returned in descending order on the `updated_at` attribute. This means if you need to iterate through results then we'll show the most recently updated collections first. + + 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 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[Collection, CollectionList] + Successful + """ + page = page if page is not None else 1 + + _response = self._client_wrapper.httpx_client.request( + "help_center/collections", + method="GET", + params={ + "page": page, + "per_page": per_page, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + CollectionList, + construct_type( + type_=CollectionList, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.data + _has_next = True + _get_next = lambda: self.list( + 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(), + ), + ), + ) + _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, + *, + name: str, + description: typing.Optional[str] = OMIT, + translated_content: typing.Optional[GroupTranslatedContent] = OMIT, + parent_id: typing.Optional[str] = OMIT, + help_center_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Collection]: + """ + You can create a new collection by making a POST request to `https://api.intercom.io/help_center/collections.` + + Parameters + ---------- + name : str + The name of the collection. For multilingual collections, this will be the name of the default language's content. + + description : typing.Optional[str] + The description of the collection. For multilingual collections, this will be the description of the default language's content. + + translated_content : typing.Optional[GroupTranslatedContent] + + parent_id : typing.Optional[str] + The id of the parent collection. If `null` then it will be created as the first level collection. + + help_center_id : typing.Optional[int] + The id of the help center where the collection will be created. If `null` then it will be created in the default help center. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Collection] + collection created + """ + _response = self._client_wrapper.httpx_client.request( + "help_center/collections", + method="POST", + json={ + "name": name, + "description": description, + "translated_content": convert_and_respect_annotation_metadata( + object_=translated_content, annotation=GroupTranslatedContent, direction="write" + ), + "parent_id": parent_id, + "help_center_id": help_center_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Collection, + construct_type( + type_=Collection, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 find( + self, collection_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Collection]: + """ + You can fetch the details of a single collection by making a GET request to `https://api.intercom.io/help_center/collections/`. + + Parameters + ---------- + collection_id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Collection] + Collection found + """ + _response = self._client_wrapper.httpx_client.request( + f"help_center/collections/{jsonable_encoder(collection_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Collection, + construct_type( + type_=Collection, # 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 update( + self, + collection_id: int, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + translated_content: typing.Optional[GroupTranslatedContent] = OMIT, + parent_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Collection]: + """ + You can update the details of a single collection by making a PUT request to `https://api.intercom.io/collections/`. + + Parameters + ---------- + collection_id : int + The unique identifier for the collection which is given by Intercom. + + name : typing.Optional[str] + The name of the collection. For multilingual collections, this will be the name of the default language's content. + + description : typing.Optional[str] + The description of the collection. For multilingual collections, this will be the description of the default language's content. + + translated_content : typing.Optional[GroupTranslatedContent] + + parent_id : typing.Optional[str] + The id of the parent collection. If `null` then it will be updated as the first level collection. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Collection] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"help_center/collections/{jsonable_encoder(collection_id)}", + method="PUT", + json={ + "name": name, + "description": description, + "translated_content": convert_and_respect_annotation_metadata( + object_=translated_content, annotation=GroupTranslatedContent, direction="write" + ), + "parent_id": parent_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Collection, + construct_type( + type_=Collection, # 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 delete( + self, collection_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DeletedCollectionObject]: + """ + You can delete a single collection by making a DELETE request to `https://api.intercom.io/collections/`. + + Parameters + ---------- + collection_id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeletedCollectionObject] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"help_center/collections/{jsonable_encoder(collection_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeletedCollectionObject, + construct_type( + type_=DeletedCollectionObject, # 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) + + +class AsyncRawCollectionsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[Collection, CollectionList]: + """ + You can fetch a list of all collections by making a GET request to `https://api.intercom.io/help_center/collections`. + + Collections will be returned in descending order on the `updated_at` attribute. This means if you need to iterate through results then we'll show the most recently updated collections first. + + 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 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[Collection, CollectionList] + Successful + """ + page = page if page is not None else 1 + + _response = await self._client_wrapper.httpx_client.request( + "help_center/collections", + method="GET", + params={ + "page": page, + "per_page": per_page, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + CollectionList, + construct_type( + type_=CollectionList, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.data + _has_next = True + + async def _get_next(): + return await self.list( + 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(), + ), + ), + ) + _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, + *, + name: str, + description: typing.Optional[str] = OMIT, + translated_content: typing.Optional[GroupTranslatedContent] = OMIT, + parent_id: typing.Optional[str] = OMIT, + help_center_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Collection]: + """ + You can create a new collection by making a POST request to `https://api.intercom.io/help_center/collections.` + + Parameters + ---------- + name : str + The name of the collection. For multilingual collections, this will be the name of the default language's content. + + description : typing.Optional[str] + The description of the collection. For multilingual collections, this will be the description of the default language's content. + + translated_content : typing.Optional[GroupTranslatedContent] + + parent_id : typing.Optional[str] + The id of the parent collection. If `null` then it will be created as the first level collection. + + help_center_id : typing.Optional[int] + The id of the help center where the collection will be created. If `null` then it will be created in the default help center. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Collection] + collection created + """ + _response = await self._client_wrapper.httpx_client.request( + "help_center/collections", + method="POST", + json={ + "name": name, + "description": description, + "translated_content": convert_and_respect_annotation_metadata( + object_=translated_content, annotation=GroupTranslatedContent, direction="write" + ), + "parent_id": parent_id, + "help_center_id": help_center_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Collection, + construct_type( + type_=Collection, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 find( + self, collection_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Collection]: + """ + You can fetch the details of a single collection by making a GET request to `https://api.intercom.io/help_center/collections/`. + + Parameters + ---------- + collection_id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Collection] + Collection found + """ + _response = await self._client_wrapper.httpx_client.request( + f"help_center/collections/{jsonable_encoder(collection_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Collection, + construct_type( + type_=Collection, # 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 update( + self, + collection_id: int, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + translated_content: typing.Optional[GroupTranslatedContent] = OMIT, + parent_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Collection]: + """ + You can update the details of a single collection by making a PUT request to `https://api.intercom.io/collections/`. + + Parameters + ---------- + collection_id : int + The unique identifier for the collection which is given by Intercom. + + name : typing.Optional[str] + The name of the collection. For multilingual collections, this will be the name of the default language's content. + + description : typing.Optional[str] + The description of the collection. For multilingual collections, this will be the description of the default language's content. + + translated_content : typing.Optional[GroupTranslatedContent] + + parent_id : typing.Optional[str] + The id of the parent collection. If `null` then it will be updated as the first level collection. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Collection] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"help_center/collections/{jsonable_encoder(collection_id)}", + method="PUT", + json={ + "name": name, + "description": description, + "translated_content": convert_and_respect_annotation_metadata( + object_=translated_content, annotation=GroupTranslatedContent, direction="write" + ), + "parent_id": parent_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Collection, + construct_type( + type_=Collection, # 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 delete( + self, collection_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DeletedCollectionObject]: + """ + You can delete a single collection by making a DELETE request to `https://api.intercom.io/collections/`. + + Parameters + ---------- + collection_id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeletedCollectionObject] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"help_center/collections/{jsonable_encoder(collection_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeletedCollectionObject, + construct_type( + type_=DeletedCollectionObject, # 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) diff --git a/src/intercom/help_centers/raw_client.py b/src/intercom/help_centers/raw_client.py new file mode 100644 index 00000000..0e27069f --- /dev/null +++ b/src/intercom/help_centers/raw_client.py @@ -0,0 +1,292 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.unchecked_base_model import construct_type +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..help_center.types.help_center import HelpCenter +from ..help_center.types.help_center_list import HelpCenterList +from ..types.error import Error + + +class RawHelpCentersClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def find( + self, help_center_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[HelpCenter]: + """ + You can fetch the details of a single Help Center by making a GET request to `https://api.intercom.io/help_center/help_center/`. + + Parameters + ---------- + help_center_id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[HelpCenter] + Collection found + """ + _response = self._client_wrapper.httpx_client.request( + f"help_center/help_centers/{jsonable_encoder(help_center_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + HelpCenter, + construct_type( + type_=HelpCenter, # 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( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[HelpCenter, HelpCenterList]: + """ + You can list all Help Centers by making a GET request to `https://api.intercom.io/help_center/help_centers`. + + 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 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[HelpCenter, HelpCenterList] + Help Centers found + """ + page = page if page is not None else 1 + + _response = self._client_wrapper.httpx_client.request( + "help_center/help_centers", + method="GET", + params={ + "page": page, + "per_page": per_page, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + HelpCenterList, + construct_type( + type_=HelpCenterList, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.data + _has_next = True + _get_next = lambda: self.list( + 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(), + ), + ), + ) + _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 AsyncRawHelpCentersClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def find( + self, help_center_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[HelpCenter]: + """ + You can fetch the details of a single Help Center by making a GET request to `https://api.intercom.io/help_center/help_center/`. + + Parameters + ---------- + help_center_id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[HelpCenter] + Collection found + """ + _response = await self._client_wrapper.httpx_client.request( + f"help_center/help_centers/{jsonable_encoder(help_center_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + HelpCenter, + construct_type( + type_=HelpCenter, # 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( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[HelpCenter, HelpCenterList]: + """ + You can list all Help Centers by making a GET request to `https://api.intercom.io/help_center/help_centers`. + + 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 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[HelpCenter, HelpCenterList] + Help Centers found + """ + page = page if page is not None else 1 + + _response = await self._client_wrapper.httpx_client.request( + "help_center/help_centers", + method="GET", + params={ + "page": page, + "per_page": per_page, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + HelpCenterList, + construct_type( + type_=HelpCenterList, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.data + _has_next = True + + async def _get_next(): + return await self.list( + 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(), + ), + ), + ) + _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) diff --git a/src/intercom/internal_articles/__init__.py b/src/intercom/internal_articles/__init__.py new file mode 100644 index 00000000..f7b287f2 --- /dev/null +++ b/src/intercom/internal_articles/__init__.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import InternalArticleListItem, InternalArticleSearchResponse, InternalArticleSearchResponseData +_dynamic_imports: typing.Dict[str, str] = { + "InternalArticleListItem": ".types", + "InternalArticleSearchResponse": ".types", + "InternalArticleSearchResponseData": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["InternalArticleListItem", "InternalArticleSearchResponse", "InternalArticleSearchResponseData"] diff --git a/src/intercom/internal_articles/client.py b/src/intercom/internal_articles/client.py new file mode 100644 index 00000000..2d3676d6 --- /dev/null +++ b/src/intercom/internal_articles/client.py @@ -0,0 +1,553 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..articles.types.internal_article import InternalArticle +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.create_internal_article_request import CreateInternalArticleRequest +from ..types.deleted_internal_article_object import DeletedInternalArticleObject +from ..types.internal_article_list import InternalArticleList +from .raw_client import AsyncRawInternalArticlesClient, RawInternalArticlesClient +from .types.internal_article_search_response import InternalArticleSearchResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class InternalArticlesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawInternalArticlesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawInternalArticlesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawInternalArticlesClient + """ + return self._raw_client + + def list_internal_articles(self, *, request_options: typing.Optional[RequestOptions] = None) -> InternalArticleList: + """ + You can fetch a list of all internal articles by making a GET request to `https://api.intercom.io/internal_articles`. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + InternalArticleList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.internal_articles.list_internal_articles() + """ + _response = self._raw_client.list_internal_articles(request_options=request_options) + return _response.data + + def create_internal_article( + self, + *, + request: typing.Optional[CreateInternalArticleRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> InternalArticle: + """ + You can create a new internal article by making a POST request to `https://api.intercom.io/internal_articles`. + + Parameters + ---------- + request : typing.Optional[CreateInternalArticleRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + InternalArticle + internal article created + + Examples + -------- + from intercom import CreateInternalArticleRequest, Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.internal_articles.create_internal_article( + request=CreateInternalArticleRequest( + title="Thanks for everything", + body="Body of the Article", + author_id=991266252, + owner_id=991266252, + ), + ) + """ + _response = self._raw_client.create_internal_article(request=request, request_options=request_options) + return _response.data + + def retrieve_internal_article( + self, internal_article_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> InternalArticle: + """ + You can fetch the details of a single internal article by making a GET request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + internal_article_id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + InternalArticle + Internal article found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.internal_articles.retrieve_internal_article( + internal_article_id=1, + ) + """ + _response = self._raw_client.retrieve_internal_article(internal_article_id, request_options=request_options) + return _response.data + + def update_internal_article( + self, + internal_article_id: int, + *, + title: typing.Optional[str] = OMIT, + body: typing.Optional[str] = OMIT, + author_id: typing.Optional[int] = OMIT, + owner_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> InternalArticle: + """ + You can update the details of a single internal article by making a PUT request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + internal_article_id : int + The unique identifier for the internal article which is given by Intercom. + + title : typing.Optional[str] + The title of the article. + + body : typing.Optional[str] + The content of the article. + + author_id : typing.Optional[int] + The id of the author of the article. + + owner_id : typing.Optional[int] + The id of the author of the article. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + InternalArticle + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.internal_articles.update_internal_article( + internal_article_id=1, + title="Christmas is here!", + body="

New gifts in store for the jolly season

", + ) + """ + _response = self._raw_client.update_internal_article( + internal_article_id, + title=title, + body=body, + author_id=author_id, + owner_id=owner_id, + request_options=request_options, + ) + return _response.data + + def delete_internal_article( + self, internal_article_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeletedInternalArticleObject: + """ + You can delete a single internal article by making a DELETE request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + internal_article_id : int + The unique identifier for the internal article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeletedInternalArticleObject + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.internal_articles.delete_internal_article( + internal_article_id=1, + ) + """ + _response = self._raw_client.delete_internal_article(internal_article_id, request_options=request_options) + return _response.data + + def search_internal_articles( + self, *, folder_id: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None + ) -> InternalArticleSearchResponse: + """ + You can search for internal articles by making a GET request to `https://api.intercom.io/internal_articles/search`. + + Parameters + ---------- + folder_id : typing.Optional[str] + The ID of the folder to search in. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + InternalArticleSearchResponse + Search successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.internal_articles.search_internal_articles( + folder_id="folder_id", + ) + """ + _response = self._raw_client.search_internal_articles(folder_id=folder_id, request_options=request_options) + return _response.data + + +class AsyncInternalArticlesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawInternalArticlesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawInternalArticlesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawInternalArticlesClient + """ + return self._raw_client + + async def list_internal_articles( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> InternalArticleList: + """ + You can fetch a list of all internal articles by making a GET request to `https://api.intercom.io/internal_articles`. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + InternalArticleList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.internal_articles.list_internal_articles() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_internal_articles(request_options=request_options) + return _response.data + + async def create_internal_article( + self, + *, + request: typing.Optional[CreateInternalArticleRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> InternalArticle: + """ + You can create a new internal article by making a POST request to `https://api.intercom.io/internal_articles`. + + Parameters + ---------- + request : typing.Optional[CreateInternalArticleRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + InternalArticle + internal article created + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom, CreateInternalArticleRequest + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.internal_articles.create_internal_article( + request=CreateInternalArticleRequest( + title="Thanks for everything", + body="Body of the Article", + author_id=991266252, + owner_id=991266252, + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_internal_article(request=request, request_options=request_options) + return _response.data + + async def retrieve_internal_article( + self, internal_article_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> InternalArticle: + """ + You can fetch the details of a single internal article by making a GET request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + internal_article_id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + InternalArticle + Internal article found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.internal_articles.retrieve_internal_article( + internal_article_id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.retrieve_internal_article( + internal_article_id, request_options=request_options + ) + return _response.data + + async def update_internal_article( + self, + internal_article_id: int, + *, + title: typing.Optional[str] = OMIT, + body: typing.Optional[str] = OMIT, + author_id: typing.Optional[int] = OMIT, + owner_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> InternalArticle: + """ + You can update the details of a single internal article by making a PUT request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + internal_article_id : int + The unique identifier for the internal article which is given by Intercom. + + title : typing.Optional[str] + The title of the article. + + body : typing.Optional[str] + The content of the article. + + author_id : typing.Optional[int] + The id of the author of the article. + + owner_id : typing.Optional[int] + The id of the author of the article. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + InternalArticle + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.internal_articles.update_internal_article( + internal_article_id=1, + title="Christmas is here!", + body="

New gifts in store for the jolly season

", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update_internal_article( + internal_article_id, + title=title, + body=body, + author_id=author_id, + owner_id=owner_id, + request_options=request_options, + ) + return _response.data + + async def delete_internal_article( + self, internal_article_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeletedInternalArticleObject: + """ + You can delete a single internal article by making a DELETE request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + internal_article_id : int + The unique identifier for the internal article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeletedInternalArticleObject + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.internal_articles.delete_internal_article( + internal_article_id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_internal_article(internal_article_id, request_options=request_options) + return _response.data + + async def search_internal_articles( + self, *, folder_id: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None + ) -> InternalArticleSearchResponse: + """ + You can search for internal articles by making a GET request to `https://api.intercom.io/internal_articles/search`. + + Parameters + ---------- + folder_id : typing.Optional[str] + The ID of the folder to search in. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + InternalArticleSearchResponse + Search successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.internal_articles.search_internal_articles( + folder_id="folder_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.search_internal_articles( + folder_id=folder_id, request_options=request_options + ) + return _response.data diff --git a/src/intercom/internal_articles/raw_client.py b/src/intercom/internal_articles/raw_client.py new file mode 100644 index 00000000..8193364f --- /dev/null +++ b/src/intercom/internal_articles/raw_client.py @@ -0,0 +1,798 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ..articles.types.internal_article import InternalArticle +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.request_options import RequestOptions +from ..core.serialization import convert_and_respect_annotation_metadata +from ..core.unchecked_base_model import construct_type +from ..errors.bad_request_error import BadRequestError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.create_internal_article_request import CreateInternalArticleRequest +from ..types.deleted_internal_article_object import DeletedInternalArticleObject +from ..types.error import Error +from ..types.internal_article_list import InternalArticleList +from .types.internal_article_search_response import InternalArticleSearchResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawInternalArticlesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_internal_articles( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[InternalArticleList]: + """ + You can fetch a list of all internal articles by making a GET request to `https://api.intercom.io/internal_articles`. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[InternalArticleList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "internal_articles", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + InternalArticleList, + construct_type( + type_=InternalArticleList, # 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 create_internal_article( + self, + *, + request: typing.Optional[CreateInternalArticleRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[InternalArticle]: + """ + You can create a new internal article by making a POST request to `https://api.intercom.io/internal_articles`. + + Parameters + ---------- + request : typing.Optional[CreateInternalArticleRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[InternalArticle] + internal article created + """ + _response = self._client_wrapper.httpx_client.request( + "internal_articles", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateInternalArticleRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + InternalArticle, + construct_type( + type_=InternalArticle, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 retrieve_internal_article( + self, internal_article_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[InternalArticle]: + """ + You can fetch the details of a single internal article by making a GET request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + internal_article_id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[InternalArticle] + Internal article found + """ + _response = self._client_wrapper.httpx_client.request( + f"internal_articles/{jsonable_encoder(internal_article_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + InternalArticle, + construct_type( + type_=InternalArticle, # 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 update_internal_article( + self, + internal_article_id: int, + *, + title: typing.Optional[str] = OMIT, + body: typing.Optional[str] = OMIT, + author_id: typing.Optional[int] = OMIT, + owner_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[InternalArticle]: + """ + You can update the details of a single internal article by making a PUT request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + internal_article_id : int + The unique identifier for the internal article which is given by Intercom. + + title : typing.Optional[str] + The title of the article. + + body : typing.Optional[str] + The content of the article. + + author_id : typing.Optional[int] + The id of the author of the article. + + owner_id : typing.Optional[int] + The id of the author of the article. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[InternalArticle] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"internal_articles/{jsonable_encoder(internal_article_id)}", + method="PUT", + json={ + "title": title, + "body": body, + "author_id": author_id, + "owner_id": owner_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + InternalArticle, + construct_type( + type_=InternalArticle, # 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 delete_internal_article( + self, internal_article_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DeletedInternalArticleObject]: + """ + You can delete a single internal article by making a DELETE request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + internal_article_id : int + The unique identifier for the internal article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeletedInternalArticleObject] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"internal_articles/{jsonable_encoder(internal_article_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeletedInternalArticleObject, + construct_type( + type_=DeletedInternalArticleObject, # 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 search_internal_articles( + self, *, folder_id: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[InternalArticleSearchResponse]: + """ + You can search for internal articles by making a GET request to `https://api.intercom.io/internal_articles/search`. + + Parameters + ---------- + folder_id : typing.Optional[str] + The ID of the folder to search in. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[InternalArticleSearchResponse] + Search successful + """ + _response = self._client_wrapper.httpx_client.request( + "internal_articles/search", + method="GET", + params={ + "folder_id": folder_id, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + InternalArticleSearchResponse, + construct_type( + type_=InternalArticleSearchResponse, # 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) + + +class AsyncRawInternalArticlesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_internal_articles( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[InternalArticleList]: + """ + You can fetch a list of all internal articles by making a GET request to `https://api.intercom.io/internal_articles`. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[InternalArticleList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "internal_articles", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + InternalArticleList, + construct_type( + type_=InternalArticleList, # 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 create_internal_article( + self, + *, + request: typing.Optional[CreateInternalArticleRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[InternalArticle]: + """ + You can create a new internal article by making a POST request to `https://api.intercom.io/internal_articles`. + + Parameters + ---------- + request : typing.Optional[CreateInternalArticleRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[InternalArticle] + internal article created + """ + _response = await self._client_wrapper.httpx_client.request( + "internal_articles", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateInternalArticleRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + InternalArticle, + construct_type( + type_=InternalArticle, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 retrieve_internal_article( + self, internal_article_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[InternalArticle]: + """ + You can fetch the details of a single internal article by making a GET request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + internal_article_id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[InternalArticle] + Internal article found + """ + _response = await self._client_wrapper.httpx_client.request( + f"internal_articles/{jsonable_encoder(internal_article_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + InternalArticle, + construct_type( + type_=InternalArticle, # 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 update_internal_article( + self, + internal_article_id: int, + *, + title: typing.Optional[str] = OMIT, + body: typing.Optional[str] = OMIT, + author_id: typing.Optional[int] = OMIT, + owner_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[InternalArticle]: + """ + You can update the details of a single internal article by making a PUT request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + internal_article_id : int + The unique identifier for the internal article which is given by Intercom. + + title : typing.Optional[str] + The title of the article. + + body : typing.Optional[str] + The content of the article. + + author_id : typing.Optional[int] + The id of the author of the article. + + owner_id : typing.Optional[int] + The id of the author of the article. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[InternalArticle] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"internal_articles/{jsonable_encoder(internal_article_id)}", + method="PUT", + json={ + "title": title, + "body": body, + "author_id": author_id, + "owner_id": owner_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + InternalArticle, + construct_type( + type_=InternalArticle, # 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 delete_internal_article( + self, internal_article_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DeletedInternalArticleObject]: + """ + You can delete a single internal article by making a DELETE request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + internal_article_id : int + The unique identifier for the internal article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeletedInternalArticleObject] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"internal_articles/{jsonable_encoder(internal_article_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeletedInternalArticleObject, + construct_type( + type_=DeletedInternalArticleObject, # 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 search_internal_articles( + self, *, folder_id: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[InternalArticleSearchResponse]: + """ + You can search for internal articles by making a GET request to `https://api.intercom.io/internal_articles/search`. + + Parameters + ---------- + folder_id : typing.Optional[str] + The ID of the folder to search in. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[InternalArticleSearchResponse] + Search successful + """ + _response = await self._client_wrapper.httpx_client.request( + "internal_articles/search", + method="GET", + params={ + "folder_id": folder_id, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + InternalArticleSearchResponse, + construct_type( + type_=InternalArticleSearchResponse, # 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) diff --git a/src/intercom/internal_articles/types/__init__.py b/src/intercom/internal_articles/types/__init__.py new file mode 100644 index 00000000..8f1745ce --- /dev/null +++ b/src/intercom/internal_articles/types/__init__.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .internal_article_list_item import InternalArticleListItem + from .internal_article_search_response import InternalArticleSearchResponse + from .internal_article_search_response_data import InternalArticleSearchResponseData +_dynamic_imports: typing.Dict[str, str] = { + "InternalArticleListItem": ".internal_article_list_item", + "InternalArticleSearchResponse": ".internal_article_search_response", + "InternalArticleSearchResponseData": ".internal_article_search_response_data", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["InternalArticleListItem", "InternalArticleSearchResponse", "InternalArticleSearchResponseData"] diff --git a/src/intercom/internal_articles/types/internal_article_list_item.py b/src/intercom/internal_articles/types/internal_article_list_item.py new file mode 100644 index 00000000..1abf2d64 --- /dev/null +++ b/src/intercom/internal_articles/types/internal_article_list_item.py @@ -0,0 +1,67 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class InternalArticleListItem(UncheckedBaseModel): + """ + The data returned about your internal articles when you list them. + """ + + type: typing.Optional[typing.Literal["internal_article"]] = pydantic.Field(default=None) + """ + The type of object - `internal_article`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the article which is given by Intercom. + """ + + title: typing.Optional[str] = pydantic.Field(default=None) + """ + The title of the article. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The body of the article in HTML. + """ + + owner_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of the owner of the article. + """ + + author_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of the author of the article. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the article was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the article was last updated. + """ + + locale: typing.Optional[str] = pydantic.Field(default=None) + """ + The default locale of the article. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/internal_articles/types/internal_article_search_response.py b/src/intercom/internal_articles/types/internal_article_search_response.py new file mode 100644 index 00000000..427225d3 --- /dev/null +++ b/src/intercom/internal_articles/types/internal_article_search_response.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.cursor_pages import CursorPages +from .internal_article_search_response_data import InternalArticleSearchResponseData + + +class InternalArticleSearchResponse(UncheckedBaseModel): + """ + The results of an Internal Article search + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object - `list`. + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + The total number of Internal Articles matching the search query + """ + + data: typing.Optional[InternalArticleSearchResponseData] = pydantic.Field(default=None) + """ + An object containing the results of the search. + """ + + pages: typing.Optional[CursorPages] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/internal_articles/types/internal_article_search_response_data.py b/src/intercom/internal_articles/types/internal_article_search_response_data.py new file mode 100644 index 00000000..3fa1f4fa --- /dev/null +++ b/src/intercom/internal_articles/types/internal_article_search_response_data.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...articles.types.internal_article import InternalArticle +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class InternalArticleSearchResponseData(UncheckedBaseModel): + """ + An object containing the results of the search. + """ + + internal_articles: typing.Optional[typing.List[InternalArticle]] = pydantic.Field(default=None) + """ + An array of Internal Article objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/jobs/__init__.py b/src/intercom/jobs/__init__.py new file mode 100644 index 00000000..386352d5 --- /dev/null +++ b/src/intercom/jobs/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import Jobs, JobsStatus +_dynamic_imports: typing.Dict[str, str] = {"Jobs": ".types", "JobsStatus": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Jobs", "JobsStatus"] diff --git a/src/intercom/jobs/client.py b/src/intercom/jobs/client.py new file mode 100644 index 00000000..e52d1033 --- /dev/null +++ b/src/intercom/jobs/client.py @@ -0,0 +1,110 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from .raw_client import AsyncRawJobsClient, RawJobsClient +from .types.jobs import Jobs + + +class JobsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawJobsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawJobsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawJobsClient + """ + return self._raw_client + + def status(self, job_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Jobs: + """ + Retrieve the status of job execution. + + Parameters + ---------- + job_id : str + The unique identifier for the job which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Jobs + Job execution status + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.jobs.status( + job_id="job_id", + ) + """ + _response = self._raw_client.status(job_id, request_options=request_options) + return _response.data + + +class AsyncJobsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawJobsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawJobsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawJobsClient + """ + return self._raw_client + + async def status(self, job_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Jobs: + """ + Retrieve the status of job execution. + + Parameters + ---------- + job_id : str + The unique identifier for the job which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Jobs + Job execution status + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.jobs.status( + job_id="job_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.status(job_id, request_options=request_options) + return _response.data diff --git a/src/intercom/jobs/raw_client.py b/src/intercom/jobs/raw_client.py new file mode 100644 index 00000000..9aed775f --- /dev/null +++ b/src/intercom/jobs/raw_client.py @@ -0,0 +1,145 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ..core.unchecked_base_model import construct_type +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from .types.jobs import Jobs + + +class RawJobsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def status(self, job_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Jobs]: + """ + Retrieve the status of job execution. + + Parameters + ---------- + job_id : str + The unique identifier for the job which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Jobs] + Job execution status + """ + _response = self._client_wrapper.httpx_client.request( + f"jobs/status/{jsonable_encoder(job_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Jobs, + construct_type( + type_=Jobs, # 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) + + +class AsyncRawJobsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def status( + self, job_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Jobs]: + """ + Retrieve the status of job execution. + + Parameters + ---------- + job_id : str + The unique identifier for the job which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Jobs] + Job execution status + """ + _response = await self._client_wrapper.httpx_client.request( + f"jobs/status/{jsonable_encoder(job_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Jobs, + construct_type( + type_=Jobs, # 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) diff --git a/src/intercom/jobs/types/__init__.py b/src/intercom/jobs/types/__init__.py new file mode 100644 index 00000000..b125152d --- /dev/null +++ b/src/intercom/jobs/types/__init__.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .jobs import Jobs + from .jobs_status import JobsStatus +_dynamic_imports: typing.Dict[str, str] = {"Jobs": ".jobs", "JobsStatus": ".jobs_status"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Jobs", "JobsStatus"] diff --git a/src/intercom/jobs/types/jobs.py b/src/intercom/jobs/types/jobs.py new file mode 100644 index 00000000..1654a669 --- /dev/null +++ b/src/intercom/jobs/types/jobs.py @@ -0,0 +1,58 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .jobs_status import JobsStatus + + +class Jobs(UncheckedBaseModel): + """ + Jobs are tasks that are processed asynchronously by the Intercom system after being enqueued via the API. This allows for efficient handling of operations that may take time to complete, such as data imports or exports. You can check the status of your jobs to monitor their progress and ensure they are completed successfully. + """ + + type: typing.Optional[typing.Literal["job"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + id: str = pydantic.Field() + """ + The id of the job that's currently being processed or has completed. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + API endpoint URL to check the job status. + """ + + status: typing.Optional[JobsStatus] = pydantic.Field(default=None) + """ + The status of the job execution. + """ + + resource_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of resource created during job execution. + """ + + resource_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the resource created during job execution (e.g. ticket id) + """ + + resource_url: typing.Optional[str] = pydantic.Field(default=None) + """ + The url of the resource created during job exeuction. Use this url to fetch the resource. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/jobs/types/jobs_status.py b/src/intercom/jobs/types/jobs_status.py new file mode 100644 index 00000000..e2b915af --- /dev/null +++ b/src/intercom/jobs/types/jobs_status.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +JobsStatus = typing.Union[typing.Literal["pending", "success", "failed"], typing.Any] diff --git a/src/intercom/messages/__init__.py b/src/intercom/messages/__init__.py new file mode 100644 index 00000000..ad071d44 --- /dev/null +++ b/src/intercom/messages/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import Message, MessageMessageType +_dynamic_imports: typing.Dict[str, str] = {"Message": ".types", "MessageMessageType": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Message", "MessageMessageType"] diff --git a/src/intercom/messages/client.py b/src/intercom/messages/client.py new file mode 100644 index 00000000..4fe8bbc8 --- /dev/null +++ b/src/intercom/messages/client.py @@ -0,0 +1,174 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.create_message_request import CreateMessageRequest +from .raw_client import AsyncRawMessagesClient, RawMessagesClient +from .types.message import Message + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class MessagesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawMessagesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawMessagesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawMessagesClient + """ + return self._raw_client + + def create( + self, + *, + request: typing.Optional[CreateMessageRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Message: + """ + You can create a message that has been initiated by an admin. The conversation can be either an in-app message or an email. + + > 🚧 Sending for visitors + > + > There can be a short delay between when a contact is created and when a contact becomes available to be messaged through the API. A 404 Not Found error will be returned in this case. + + This will return the Message model that has been created. + + > 🚧 Retrieving Associated Conversations + > + > As this is a message, there will be no conversation present until the contact responds. Once they do, you will have to search for a contact's conversations with the id of the message. + + Parameters + ---------- + request : typing.Optional[CreateMessageRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Message + admin message created + + Examples + -------- + from intercom import ( + CreateMessageRequest_Email, + CreateMessageRequestFrom, + CreateMessageRequestTo, + Intercom, + ) + + client = Intercom( + token="YOUR_TOKEN", + ) + client.messages.create( + request=CreateMessageRequest_Email( + subject="Thanks for everything", + body="Hello there", + template="plain", + from_=CreateMessageRequestFrom( + id=394051, + ), + to=CreateMessageRequestTo( + type="user", + id="536e564f316c83104c000020", + ), + ), + ) + """ + _response = self._raw_client.create(request=request, request_options=request_options) + return _response.data + + +class AsyncMessagesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawMessagesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawMessagesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawMessagesClient + """ + return self._raw_client + + async def create( + self, + *, + request: typing.Optional[CreateMessageRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Message: + """ + You can create a message that has been initiated by an admin. The conversation can be either an in-app message or an email. + + > 🚧 Sending for visitors + > + > There can be a short delay between when a contact is created and when a contact becomes available to be messaged through the API. A 404 Not Found error will be returned in this case. + + This will return the Message model that has been created. + + > 🚧 Retrieving Associated Conversations + > + > As this is a message, there will be no conversation present until the contact responds. Once they do, you will have to search for a contact's conversations with the id of the message. + + Parameters + ---------- + request : typing.Optional[CreateMessageRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Message + admin message created + + Examples + -------- + import asyncio + + from intercom import ( + AsyncIntercom, + CreateMessageRequest_Email, + CreateMessageRequestFrom, + CreateMessageRequestTo, + ) + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.messages.create( + request=CreateMessageRequest_Email( + subject="Thanks for everything", + body="Hello there", + template="plain", + from_=CreateMessageRequestFrom( + id=394051, + ), + to=CreateMessageRequestTo( + type="user", + id="536e564f316c83104c000020", + ), + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create(request=request, request_options=request_options) + return _response.data diff --git a/src/intercom/messages/raw_client.py b/src/intercom/messages/raw_client.py new file mode 100644 index 00000000..679a30f4 --- /dev/null +++ b/src/intercom/messages/raw_client.py @@ -0,0 +1,235 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.http_response import AsyncHttpResponse, HttpResponse +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.bad_request_error import BadRequestError +from ..errors.forbidden_error import ForbiddenError +from ..errors.unauthorized_error import UnauthorizedError +from ..errors.unprocessable_entity_error import UnprocessableEntityError +from ..types.create_message_request import CreateMessageRequest +from ..types.error import Error +from .types.message import Message + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawMessagesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def create( + self, + *, + request: typing.Optional[CreateMessageRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Message]: + """ + You can create a message that has been initiated by an admin. The conversation can be either an in-app message or an email. + + > 🚧 Sending for visitors + > + > There can be a short delay between when a contact is created and when a contact becomes available to be messaged through the API. A 404 Not Found error will be returned in this case. + + This will return the Message model that has been created. + + > 🚧 Retrieving Associated Conversations + > + > As this is a message, there will be no conversation present until the contact responds. Once they do, you will have to search for a contact's conversations with the id of the message. + + Parameters + ---------- + request : typing.Optional[CreateMessageRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Message] + admin message created + """ + _response = self._client_wrapper.httpx_client.request( + "messages", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateMessageRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Message, + construct_type( + type_=Message, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + Error, + construct_type( + type_=Error, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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) + + +class AsyncRawMessagesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def create( + self, + *, + request: typing.Optional[CreateMessageRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Message]: + """ + You can create a message that has been initiated by an admin. The conversation can be either an in-app message or an email. + + > 🚧 Sending for visitors + > + > There can be a short delay between when a contact is created and when a contact becomes available to be messaged through the API. A 404 Not Found error will be returned in this case. + + This will return the Message model that has been created. + + > 🚧 Retrieving Associated Conversations + > + > As this is a message, there will be no conversation present until the contact responds. Once they do, you will have to search for a contact's conversations with the id of the message. + + Parameters + ---------- + request : typing.Optional[CreateMessageRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Message] + admin message created + """ + _response = await self._client_wrapper.httpx_client.request( + "messages", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateMessageRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Message, + construct_type( + type_=Message, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + Error, + construct_type( + type_=Error, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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) diff --git a/src/intercom/messages/types/__init__.py b/src/intercom/messages/types/__init__.py new file mode 100644 index 00000000..cfd041bb --- /dev/null +++ b/src/intercom/messages/types/__init__.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .message import Message + from .message_message_type import MessageMessageType +_dynamic_imports: typing.Dict[str, str] = {"Message": ".message", "MessageMessageType": ".message_message_type"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Message", "MessageMessageType"] diff --git a/src/intercom/messages/types/message.py b/src/intercom/messages/types/message.py new file mode 100644 index 00000000..289ece70 --- /dev/null +++ b/src/intercom/messages/types/message.py @@ -0,0 +1,58 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .message_message_type import MessageMessageType + + +class Message(UncheckedBaseModel): + """ + Message are how you reach out to contacts in Intercom. They are created when an admin sends an outbound message to a contact. + """ + + type: str = pydantic.Field() + """ + The type of the message + """ + + id: str = pydantic.Field() + """ + The id representing the message. + """ + + created_at: int = pydantic.Field() + """ + The time the conversation was created. + """ + + subject: typing.Optional[str] = pydantic.Field(default=None) + """ + The subject of the message. Only present if message_type: email. + """ + + body: str = pydantic.Field() + """ + The message body, which may contain HTML. + """ + + message_type: MessageMessageType = pydantic.Field() + """ + The type of message that was sent. Can be email, inapp, facebook or twitter. + """ + + conversation_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The associated conversation_id + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/messages/types/message_message_type.py b/src/intercom/messages/types/message_message_type.py new file mode 100644 index 00000000..ca644c78 --- /dev/null +++ b/src/intercom/messages/types/message_message_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +MessageMessageType = typing.Union[typing.Literal["email", "inapp", "facebook", "twitter"], typing.Any] diff --git a/src/intercom/news/__init__.py b/src/intercom/news/__init__.py new file mode 100644 index 00000000..4f7bf1ad --- /dev/null +++ b/src/intercom/news/__init__.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import NewsItem, NewsItemState, Newsfeed, NewsfeedAssignment + from . import feeds, items +_dynamic_imports: typing.Dict[str, str] = { + "NewsItem": ".types", + "NewsItemState": ".types", + "Newsfeed": ".types", + "NewsfeedAssignment": ".types", + "feeds": ".feeds", + "items": ".items", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["NewsItem", "NewsItemState", "Newsfeed", "NewsfeedAssignment", "feeds", "items"] diff --git a/src/intercom/news/client.py b/src/intercom/news/client.py new file mode 100644 index 00000000..aa55c628 --- /dev/null +++ b/src/intercom/news/client.py @@ -0,0 +1,82 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from .raw_client import AsyncRawNewsClient, RawNewsClient + +if typing.TYPE_CHECKING: + from .feeds.client import AsyncFeedsClient, FeedsClient + from .items.client import AsyncItemsClient, ItemsClient + + +class NewsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawNewsClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._items: typing.Optional[ItemsClient] = None + self._feeds: typing.Optional[FeedsClient] = None + + @property + def with_raw_response(self) -> RawNewsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawNewsClient + """ + return self._raw_client + + @property + def items(self): + if self._items is None: + from .items.client import ItemsClient # noqa: E402 + + self._items = ItemsClient(client_wrapper=self._client_wrapper) + return self._items + + @property + def feeds(self): + if self._feeds is None: + from .feeds.client import FeedsClient # noqa: E402 + + self._feeds = FeedsClient(client_wrapper=self._client_wrapper) + return self._feeds + + +class AsyncNewsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawNewsClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._items: typing.Optional[AsyncItemsClient] = None + self._feeds: typing.Optional[AsyncFeedsClient] = None + + @property + def with_raw_response(self) -> AsyncRawNewsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawNewsClient + """ + return self._raw_client + + @property + def items(self): + if self._items is None: + from .items.client import AsyncItemsClient # noqa: E402 + + self._items = AsyncItemsClient(client_wrapper=self._client_wrapper) + return self._items + + @property + def feeds(self): + if self._feeds is None: + from .feeds.client import AsyncFeedsClient # noqa: E402 + + self._feeds = AsyncFeedsClient(client_wrapper=self._client_wrapper) + return self._feeds diff --git a/src/intercom/news/feeds/__init__.py b/src/intercom/news/feeds/__init__.py new file mode 100644 index 00000000..5cde0202 --- /dev/null +++ b/src/intercom/news/feeds/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/src/intercom/news/feeds/client.py b/src/intercom/news/feeds/client.py new file mode 100644 index 00000000..83cff99f --- /dev/null +++ b/src/intercom/news/feeds/client.py @@ -0,0 +1,245 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ...types.paginated_response import PaginatedResponse +from ..types.newsfeed import Newsfeed +from .raw_client import AsyncRawFeedsClient, RawFeedsClient + + +class FeedsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawFeedsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawFeedsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawFeedsClient + """ + return self._raw_client + + def list_items( + self, newsfeed_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> PaginatedResponse: + """ + You can fetch a list of all news items that are live on a given newsfeed + + Parameters + ---------- + newsfeed_id : str + The unique identifier for the news feed item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.news.feeds.list_items( + newsfeed_id="123", + ) + """ + _response = self._raw_client.list_items(newsfeed_id, request_options=request_options) + return _response.data + + def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> PaginatedResponse: + """ + You can fetch a list of all newsfeeds + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.news.feeds.list() + """ + _response = self._raw_client.list(request_options=request_options) + return _response.data + + def find(self, newsfeed_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Newsfeed: + """ + You can fetch the details of a single newsfeed + + Parameters + ---------- + newsfeed_id : str + The unique identifier for the news feed item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Newsfeed + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.news.feeds.find( + newsfeed_id="123", + ) + """ + _response = self._raw_client.find(newsfeed_id, request_options=request_options) + return _response.data + + +class AsyncFeedsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawFeedsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawFeedsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawFeedsClient + """ + return self._raw_client + + async def list_items( + self, newsfeed_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> PaginatedResponse: + """ + You can fetch a list of all news items that are live on a given newsfeed + + Parameters + ---------- + newsfeed_id : str + The unique identifier for the news feed item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.news.feeds.list_items( + newsfeed_id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_items(newsfeed_id, request_options=request_options) + return _response.data + + async def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> PaginatedResponse: + """ + You can fetch a list of all newsfeeds + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.news.feeds.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list(request_options=request_options) + return _response.data + + async def find(self, newsfeed_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Newsfeed: + """ + You can fetch the details of a single newsfeed + + Parameters + ---------- + newsfeed_id : str + The unique identifier for the news feed item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Newsfeed + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.news.feeds.find( + newsfeed_id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.find(newsfeed_id, request_options=request_options) + return _response.data diff --git a/src/intercom/news/feeds/raw_client.py b/src/intercom/news/feeds/raw_client.py new file mode 100644 index 00000000..6f422e8d --- /dev/null +++ b/src/intercom/news/feeds/raw_client.py @@ -0,0 +1,317 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ...errors.unauthorized_error import UnauthorizedError +from ...types.error import Error +from ...types.paginated_response import PaginatedResponse +from ..types.newsfeed import Newsfeed + + +class RawFeedsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_items( + self, newsfeed_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[PaginatedResponse]: + """ + You can fetch a list of all news items that are live on a given newsfeed + + Parameters + ---------- + newsfeed_id : str + The unique identifier for the news feed item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[PaginatedResponse] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"news/newsfeeds/{jsonable_encoder(newsfeed_id)}/items", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedResponse, + construct_type( + type_=PaginatedResponse, # 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 list(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[PaginatedResponse]: + """ + You can fetch a list of all newsfeeds + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[PaginatedResponse] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "news/newsfeeds", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedResponse, + construct_type( + type_=PaginatedResponse, # 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 find( + self, newsfeed_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Newsfeed]: + """ + You can fetch the details of a single newsfeed + + Parameters + ---------- + newsfeed_id : str + The unique identifier for the news feed item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Newsfeed] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"news/newsfeeds/{jsonable_encoder(newsfeed_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Newsfeed, + construct_type( + type_=Newsfeed, # 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) + + +class AsyncRawFeedsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_items( + self, newsfeed_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[PaginatedResponse]: + """ + You can fetch a list of all news items that are live on a given newsfeed + + Parameters + ---------- + newsfeed_id : str + The unique identifier for the news feed item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[PaginatedResponse] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"news/newsfeeds/{jsonable_encoder(newsfeed_id)}/items", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedResponse, + construct_type( + type_=PaginatedResponse, # 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 list( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[PaginatedResponse]: + """ + You can fetch a list of all newsfeeds + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[PaginatedResponse] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "news/newsfeeds", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedResponse, + construct_type( + type_=PaginatedResponse, # 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 find( + self, newsfeed_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Newsfeed]: + """ + You can fetch the details of a single newsfeed + + Parameters + ---------- + newsfeed_id : str + The unique identifier for the news feed item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Newsfeed] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"news/newsfeeds/{jsonable_encoder(newsfeed_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Newsfeed, + construct_type( + type_=Newsfeed, # 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) diff --git a/src/intercom/news/items/__init__.py b/src/intercom/news/items/__init__.py new file mode 100644 index 00000000..5cde0202 --- /dev/null +++ b/src/intercom/news/items/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/src/intercom/news/items/client.py b/src/intercom/news/items/client.py new file mode 100644 index 00000000..66b693d5 --- /dev/null +++ b/src/intercom/news/items/client.py @@ -0,0 +1,601 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ...types.deleted_object import DeletedObject +from ...types.news_item_request_state import NewsItemRequestState +from ...types.paginated_response import PaginatedResponse +from ..types.news_item import NewsItem +from ..types.newsfeed_assignment import NewsfeedAssignment +from .raw_client import AsyncRawItemsClient, RawItemsClient + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class ItemsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawItemsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawItemsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawItemsClient + """ + return self._raw_client + + def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> PaginatedResponse: + """ + You can fetch a list of all news items + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.news.items.list() + """ + _response = self._raw_client.list(request_options=request_options) + return _response.data + + def create( + self, + *, + title: str, + sender_id: int, + body: typing.Optional[str] = OMIT, + state: typing.Optional[NewsItemRequestState] = OMIT, + deliver_silently: typing.Optional[bool] = OMIT, + labels: typing.Optional[typing.Sequence[str]] = OMIT, + reactions: typing.Optional[typing.Sequence[typing.Optional[str]]] = OMIT, + newsfeed_assignments: typing.Optional[typing.Sequence[NewsfeedAssignment]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> NewsItem: + """ + You can create a news item + + Parameters + ---------- + title : str + The title of the news item. + + sender_id : int + The id of the sender of the news item. Must be a teammate on the workspace. + + body : typing.Optional[str] + The news item body, which may contain HTML. + + state : typing.Optional[NewsItemRequestState] + News items will not be visible to your users in the assigned newsfeeds until they are set live. + + deliver_silently : typing.Optional[bool] + When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + + labels : typing.Optional[typing.Sequence[str]] + Label names displayed to users to categorize the news item. + + reactions : typing.Optional[typing.Sequence[typing.Optional[str]]] + Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + + newsfeed_assignments : typing.Optional[typing.Sequence[NewsfeedAssignment]] + A list of newsfeed_assignments to assign to the specified newsfeed. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + NewsItem + successful + + Examples + -------- + from intercom import Intercom + from intercom.news import NewsfeedAssignment + + client = Intercom( + token="YOUR_TOKEN", + ) + client.news.items.create( + title="Halloween is here!", + body="

New costumes in store for this spooky season

", + sender_id=991267834, + state="live", + deliver_silently=True, + labels=["Product", "Update", "New"], + reactions=["😆", "😅"], + newsfeed_assignments=[ + NewsfeedAssignment( + newsfeed_id=53, + published_at=1664638214, + ) + ], + ) + """ + _response = self._raw_client.create( + title=title, + sender_id=sender_id, + body=body, + state=state, + deliver_silently=deliver_silently, + labels=labels, + reactions=reactions, + newsfeed_assignments=newsfeed_assignments, + request_options=request_options, + ) + return _response.data + + def find(self, news_item_id: int, *, request_options: typing.Optional[RequestOptions] = None) -> NewsItem: + """ + You can fetch the details of a single news item. + + Parameters + ---------- + news_item_id : int + The unique identifier for the news item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + NewsItem + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.news.items.find( + news_item_id=1, + ) + """ + _response = self._raw_client.find(news_item_id, request_options=request_options) + return _response.data + + def update( + self, + news_item_id: int, + *, + title: str, + sender_id: int, + body: typing.Optional[str] = OMIT, + state: typing.Optional[NewsItemRequestState] = OMIT, + deliver_silently: typing.Optional[bool] = OMIT, + labels: typing.Optional[typing.Sequence[str]] = OMIT, + reactions: typing.Optional[typing.Sequence[typing.Optional[str]]] = OMIT, + newsfeed_assignments: typing.Optional[typing.Sequence[NewsfeedAssignment]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> NewsItem: + """ + Parameters + ---------- + news_item_id : int + The unique identifier for the news item which is given by Intercom. + + title : str + The title of the news item. + + sender_id : int + The id of the sender of the news item. Must be a teammate on the workspace. + + body : typing.Optional[str] + The news item body, which may contain HTML. + + state : typing.Optional[NewsItemRequestState] + News items will not be visible to your users in the assigned newsfeeds until they are set live. + + deliver_silently : typing.Optional[bool] + When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + + labels : typing.Optional[typing.Sequence[str]] + Label names displayed to users to categorize the news item. + + reactions : typing.Optional[typing.Sequence[typing.Optional[str]]] + Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + + newsfeed_assignments : typing.Optional[typing.Sequence[NewsfeedAssignment]] + A list of newsfeed_assignments to assign to the specified newsfeed. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + NewsItem + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.news.items.update( + news_item_id=1, + title="Christmas is here!", + body="

New gifts in store for the jolly season

", + sender_id=991267845, + reactions=["😝", "😂"], + ) + """ + _response = self._raw_client.update( + news_item_id, + title=title, + sender_id=sender_id, + body=body, + state=state, + deliver_silently=deliver_silently, + labels=labels, + reactions=reactions, + newsfeed_assignments=newsfeed_assignments, + request_options=request_options, + ) + return _response.data + + def delete(self, news_item_id: int, *, request_options: typing.Optional[RequestOptions] = None) -> DeletedObject: + """ + You can delete a single news item. + + Parameters + ---------- + news_item_id : int + The unique identifier for the news item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeletedObject + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.news.items.delete( + news_item_id=1, + ) + """ + _response = self._raw_client.delete(news_item_id, request_options=request_options) + return _response.data + + +class AsyncItemsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawItemsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawItemsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawItemsClient + """ + return self._raw_client + + async def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> PaginatedResponse: + """ + You can fetch a list of all news items + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.news.items.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list(request_options=request_options) + return _response.data + + async def create( + self, + *, + title: str, + sender_id: int, + body: typing.Optional[str] = OMIT, + state: typing.Optional[NewsItemRequestState] = OMIT, + deliver_silently: typing.Optional[bool] = OMIT, + labels: typing.Optional[typing.Sequence[str]] = OMIT, + reactions: typing.Optional[typing.Sequence[typing.Optional[str]]] = OMIT, + newsfeed_assignments: typing.Optional[typing.Sequence[NewsfeedAssignment]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> NewsItem: + """ + You can create a news item + + Parameters + ---------- + title : str + The title of the news item. + + sender_id : int + The id of the sender of the news item. Must be a teammate on the workspace. + + body : typing.Optional[str] + The news item body, which may contain HTML. + + state : typing.Optional[NewsItemRequestState] + News items will not be visible to your users in the assigned newsfeeds until they are set live. + + deliver_silently : typing.Optional[bool] + When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + + labels : typing.Optional[typing.Sequence[str]] + Label names displayed to users to categorize the news item. + + reactions : typing.Optional[typing.Sequence[typing.Optional[str]]] + Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + + newsfeed_assignments : typing.Optional[typing.Sequence[NewsfeedAssignment]] + A list of newsfeed_assignments to assign to the specified newsfeed. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + NewsItem + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.news import NewsfeedAssignment + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.news.items.create( + title="Halloween is here!", + body="

New costumes in store for this spooky season

", + sender_id=991267834, + state="live", + deliver_silently=True, + labels=["Product", "Update", "New"], + reactions=["😆", "😅"], + newsfeed_assignments=[ + NewsfeedAssignment( + newsfeed_id=53, + published_at=1664638214, + ) + ], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + title=title, + sender_id=sender_id, + body=body, + state=state, + deliver_silently=deliver_silently, + labels=labels, + reactions=reactions, + newsfeed_assignments=newsfeed_assignments, + request_options=request_options, + ) + return _response.data + + async def find(self, news_item_id: int, *, request_options: typing.Optional[RequestOptions] = None) -> NewsItem: + """ + You can fetch the details of a single news item. + + Parameters + ---------- + news_item_id : int + The unique identifier for the news item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + NewsItem + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.news.items.find( + news_item_id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.find(news_item_id, request_options=request_options) + return _response.data + + async def update( + self, + news_item_id: int, + *, + title: str, + sender_id: int, + body: typing.Optional[str] = OMIT, + state: typing.Optional[NewsItemRequestState] = OMIT, + deliver_silently: typing.Optional[bool] = OMIT, + labels: typing.Optional[typing.Sequence[str]] = OMIT, + reactions: typing.Optional[typing.Sequence[typing.Optional[str]]] = OMIT, + newsfeed_assignments: typing.Optional[typing.Sequence[NewsfeedAssignment]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> NewsItem: + """ + Parameters + ---------- + news_item_id : int + The unique identifier for the news item which is given by Intercom. + + title : str + The title of the news item. + + sender_id : int + The id of the sender of the news item. Must be a teammate on the workspace. + + body : typing.Optional[str] + The news item body, which may contain HTML. + + state : typing.Optional[NewsItemRequestState] + News items will not be visible to your users in the assigned newsfeeds until they are set live. + + deliver_silently : typing.Optional[bool] + When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + + labels : typing.Optional[typing.Sequence[str]] + Label names displayed to users to categorize the news item. + + reactions : typing.Optional[typing.Sequence[typing.Optional[str]]] + Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + + newsfeed_assignments : typing.Optional[typing.Sequence[NewsfeedAssignment]] + A list of newsfeed_assignments to assign to the specified newsfeed. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + NewsItem + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.news.items.update( + news_item_id=1, + title="Christmas is here!", + body="

New gifts in store for the jolly season

", + sender_id=991267845, + reactions=["😝", "😂"], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update( + news_item_id, + title=title, + sender_id=sender_id, + body=body, + state=state, + deliver_silently=deliver_silently, + labels=labels, + reactions=reactions, + newsfeed_assignments=newsfeed_assignments, + request_options=request_options, + ) + return _response.data + + async def delete( + self, news_item_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeletedObject: + """ + You can delete a single news item. + + Parameters + ---------- + news_item_id : int + The unique identifier for the news item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeletedObject + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.news.items.delete( + news_item_id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete(news_item_id, request_options=request_options) + return _response.data diff --git a/src/intercom/news/items/raw_client.py b/src/intercom/news/items/raw_client.py new file mode 100644 index 00000000..d95a0860 --- /dev/null +++ b/src/intercom/news/items/raw_client.py @@ -0,0 +1,783 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.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 ...types.deleted_object import DeletedObject +from ...types.error import Error +from ...types.news_item_request_state import NewsItemRequestState +from ...types.paginated_response import PaginatedResponse +from ..types.news_item import NewsItem +from ..types.newsfeed_assignment import NewsfeedAssignment + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawItemsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[PaginatedResponse]: + """ + You can fetch a list of all news items + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[PaginatedResponse] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "news/news_items", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedResponse, + construct_type( + type_=PaginatedResponse, # 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 create( + self, + *, + title: str, + sender_id: int, + body: typing.Optional[str] = OMIT, + state: typing.Optional[NewsItemRequestState] = OMIT, + deliver_silently: typing.Optional[bool] = OMIT, + labels: typing.Optional[typing.Sequence[str]] = OMIT, + reactions: typing.Optional[typing.Sequence[typing.Optional[str]]] = OMIT, + newsfeed_assignments: typing.Optional[typing.Sequence[NewsfeedAssignment]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[NewsItem]: + """ + You can create a news item + + Parameters + ---------- + title : str + The title of the news item. + + sender_id : int + The id of the sender of the news item. Must be a teammate on the workspace. + + body : typing.Optional[str] + The news item body, which may contain HTML. + + state : typing.Optional[NewsItemRequestState] + News items will not be visible to your users in the assigned newsfeeds until they are set live. + + deliver_silently : typing.Optional[bool] + When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + + labels : typing.Optional[typing.Sequence[str]] + Label names displayed to users to categorize the news item. + + reactions : typing.Optional[typing.Sequence[typing.Optional[str]]] + Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + + newsfeed_assignments : typing.Optional[typing.Sequence[NewsfeedAssignment]] + A list of newsfeed_assignments to assign to the specified newsfeed. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[NewsItem] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "news/news_items", + method="POST", + json={ + "title": title, + "body": body, + "sender_id": sender_id, + "state": state, + "deliver_silently": deliver_silently, + "labels": labels, + "reactions": reactions, + "newsfeed_assignments": convert_and_respect_annotation_metadata( + object_=newsfeed_assignments, annotation=typing.Sequence[NewsfeedAssignment], direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + NewsItem, + construct_type( + type_=NewsItem, # 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 find( + self, news_item_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[NewsItem]: + """ + You can fetch the details of a single news item. + + Parameters + ---------- + news_item_id : int + The unique identifier for the news item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[NewsItem] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"news/news_items/{jsonable_encoder(news_item_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + NewsItem, + construct_type( + type_=NewsItem, # 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 update( + self, + news_item_id: int, + *, + title: str, + sender_id: int, + body: typing.Optional[str] = OMIT, + state: typing.Optional[NewsItemRequestState] = OMIT, + deliver_silently: typing.Optional[bool] = OMIT, + labels: typing.Optional[typing.Sequence[str]] = OMIT, + reactions: typing.Optional[typing.Sequence[typing.Optional[str]]] = OMIT, + newsfeed_assignments: typing.Optional[typing.Sequence[NewsfeedAssignment]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[NewsItem]: + """ + Parameters + ---------- + news_item_id : int + The unique identifier for the news item which is given by Intercom. + + title : str + The title of the news item. + + sender_id : int + The id of the sender of the news item. Must be a teammate on the workspace. + + body : typing.Optional[str] + The news item body, which may contain HTML. + + state : typing.Optional[NewsItemRequestState] + News items will not be visible to your users in the assigned newsfeeds until they are set live. + + deliver_silently : typing.Optional[bool] + When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + + labels : typing.Optional[typing.Sequence[str]] + Label names displayed to users to categorize the news item. + + reactions : typing.Optional[typing.Sequence[typing.Optional[str]]] + Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + + newsfeed_assignments : typing.Optional[typing.Sequence[NewsfeedAssignment]] + A list of newsfeed_assignments to assign to the specified newsfeed. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[NewsItem] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"news/news_items/{jsonable_encoder(news_item_id)}", + method="PUT", + json={ + "title": title, + "body": body, + "sender_id": sender_id, + "state": state, + "deliver_silently": deliver_silently, + "labels": labels, + "reactions": reactions, + "newsfeed_assignments": convert_and_respect_annotation_metadata( + object_=newsfeed_assignments, annotation=typing.Sequence[NewsfeedAssignment], direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + NewsItem, + construct_type( + type_=NewsItem, # 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 delete( + self, news_item_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DeletedObject]: + """ + You can delete a single news item. + + Parameters + ---------- + news_item_id : int + The unique identifier for the news item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeletedObject] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"news/news_items/{jsonable_encoder(news_item_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeletedObject, + construct_type( + type_=DeletedObject, # 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) + + +class AsyncRawItemsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[PaginatedResponse]: + """ + You can fetch a list of all news items + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[PaginatedResponse] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "news/news_items", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedResponse, + construct_type( + type_=PaginatedResponse, # 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 create( + self, + *, + title: str, + sender_id: int, + body: typing.Optional[str] = OMIT, + state: typing.Optional[NewsItemRequestState] = OMIT, + deliver_silently: typing.Optional[bool] = OMIT, + labels: typing.Optional[typing.Sequence[str]] = OMIT, + reactions: typing.Optional[typing.Sequence[typing.Optional[str]]] = OMIT, + newsfeed_assignments: typing.Optional[typing.Sequence[NewsfeedAssignment]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[NewsItem]: + """ + You can create a news item + + Parameters + ---------- + title : str + The title of the news item. + + sender_id : int + The id of the sender of the news item. Must be a teammate on the workspace. + + body : typing.Optional[str] + The news item body, which may contain HTML. + + state : typing.Optional[NewsItemRequestState] + News items will not be visible to your users in the assigned newsfeeds until they are set live. + + deliver_silently : typing.Optional[bool] + When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + + labels : typing.Optional[typing.Sequence[str]] + Label names displayed to users to categorize the news item. + + reactions : typing.Optional[typing.Sequence[typing.Optional[str]]] + Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + + newsfeed_assignments : typing.Optional[typing.Sequence[NewsfeedAssignment]] + A list of newsfeed_assignments to assign to the specified newsfeed. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[NewsItem] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "news/news_items", + method="POST", + json={ + "title": title, + "body": body, + "sender_id": sender_id, + "state": state, + "deliver_silently": deliver_silently, + "labels": labels, + "reactions": reactions, + "newsfeed_assignments": convert_and_respect_annotation_metadata( + object_=newsfeed_assignments, annotation=typing.Sequence[NewsfeedAssignment], direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + NewsItem, + construct_type( + type_=NewsItem, # 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 find( + self, news_item_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[NewsItem]: + """ + You can fetch the details of a single news item. + + Parameters + ---------- + news_item_id : int + The unique identifier for the news item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[NewsItem] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"news/news_items/{jsonable_encoder(news_item_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + NewsItem, + construct_type( + type_=NewsItem, # 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 update( + self, + news_item_id: int, + *, + title: str, + sender_id: int, + body: typing.Optional[str] = OMIT, + state: typing.Optional[NewsItemRequestState] = OMIT, + deliver_silently: typing.Optional[bool] = OMIT, + labels: typing.Optional[typing.Sequence[str]] = OMIT, + reactions: typing.Optional[typing.Sequence[typing.Optional[str]]] = OMIT, + newsfeed_assignments: typing.Optional[typing.Sequence[NewsfeedAssignment]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[NewsItem]: + """ + Parameters + ---------- + news_item_id : int + The unique identifier for the news item which is given by Intercom. + + title : str + The title of the news item. + + sender_id : int + The id of the sender of the news item. Must be a teammate on the workspace. + + body : typing.Optional[str] + The news item body, which may contain HTML. + + state : typing.Optional[NewsItemRequestState] + News items will not be visible to your users in the assigned newsfeeds until they are set live. + + deliver_silently : typing.Optional[bool] + When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + + labels : typing.Optional[typing.Sequence[str]] + Label names displayed to users to categorize the news item. + + reactions : typing.Optional[typing.Sequence[typing.Optional[str]]] + Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + + newsfeed_assignments : typing.Optional[typing.Sequence[NewsfeedAssignment]] + A list of newsfeed_assignments to assign to the specified newsfeed. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[NewsItem] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"news/news_items/{jsonable_encoder(news_item_id)}", + method="PUT", + json={ + "title": title, + "body": body, + "sender_id": sender_id, + "state": state, + "deliver_silently": deliver_silently, + "labels": labels, + "reactions": reactions, + "newsfeed_assignments": convert_and_respect_annotation_metadata( + object_=newsfeed_assignments, annotation=typing.Sequence[NewsfeedAssignment], direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + NewsItem, + construct_type( + type_=NewsItem, # 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 delete( + self, news_item_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DeletedObject]: + """ + You can delete a single news item. + + Parameters + ---------- + news_item_id : int + The unique identifier for the news item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeletedObject] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"news/news_items/{jsonable_encoder(news_item_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeletedObject, + construct_type( + type_=DeletedObject, # 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) diff --git a/src/intercom/news/raw_client.py b/src/intercom/news/raw_client.py new file mode 100644 index 00000000..a60e2ffc --- /dev/null +++ b/src/intercom/news/raw_client.py @@ -0,0 +1,13 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper + + +class RawNewsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + +class AsyncRawNewsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper diff --git a/src/intercom/news/types/__init__.py b/src/intercom/news/types/__init__.py new file mode 100644 index 00000000..1af0e8e4 --- /dev/null +++ b/src/intercom/news/types/__init__.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .news_item import NewsItem + from .news_item_state import NewsItemState + from .newsfeed import Newsfeed + from .newsfeed_assignment import NewsfeedAssignment +_dynamic_imports: typing.Dict[str, str] = { + "NewsItem": ".news_item", + "NewsItemState": ".news_item_state", + "Newsfeed": ".newsfeed", + "NewsfeedAssignment": ".newsfeed_assignment", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["NewsItem", "NewsItemState", "Newsfeed", "NewsfeedAssignment"] diff --git a/src/intercom/news/types/news_item.py b/src/intercom/news/types/news_item.py new file mode 100644 index 00000000..a461e5b7 --- /dev/null +++ b/src/intercom/news/types/news_item.py @@ -0,0 +1,94 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .news_item_state import NewsItemState +from .newsfeed_assignment import NewsfeedAssignment + + +class NewsItem(UncheckedBaseModel): + """ + A News Item is a content type in Intercom enabling you to announce product updates, company news, promotions, events and more with your customers. + """ + + type: typing.Optional[typing.Literal["news-item"]] = pydantic.Field(default=None) + """ + The type of object. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the news item which is given by Intercom. + """ + + workspace_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the workspace which the news item belongs to. + """ + + title: typing.Optional[str] = pydantic.Field(default=None) + """ + The title of the news item. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The news item body, which may contain HTML. + """ + + sender_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of the sender of the news item. Must be a teammate on the workspace. + """ + + state: typing.Optional[NewsItemState] = pydantic.Field(default=None) + """ + News items will not be visible to your users in the assigned newsfeeds until they are set live. + """ + + newsfeed_assignments: typing.Optional[typing.List[NewsfeedAssignment]] = pydantic.Field(default=None) + """ + A list of newsfeed_assignments to assign to the specified newsfeed. + """ + + labels: typing.Optional[typing.List[typing.Optional[str]]] = pydantic.Field(default=None) + """ + Label names displayed to users to categorize the news item. + """ + + cover_image_url: typing.Optional[str] = pydantic.Field(default=None) + """ + URL of the image used as cover. Must have .jpg or .png extension. + """ + + reactions: typing.Optional[typing.List[typing.Optional[str]]] = pydantic.Field(default=None) + """ + Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + """ + + deliver_silently: typing.Optional[bool] = pydantic.Field(default=None) + """ + When set to true, the news item will appear in the messenger newsfeed without showing a notification badge. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Timestamp for when the news item was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Timestamp for when the news item was last updated. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/news/types/news_item_state.py b/src/intercom/news/types/news_item_state.py new file mode 100644 index 00000000..2b16536c --- /dev/null +++ b/src/intercom/news/types/news_item_state.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +NewsItemState = typing.Union[typing.Literal["draft", "live"], typing.Any] diff --git a/src/intercom/news/types/newsfeed.py b/src/intercom/news/types/newsfeed.py new file mode 100644 index 00000000..50d5502d --- /dev/null +++ b/src/intercom/news/types/newsfeed.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class Newsfeed(UncheckedBaseModel): + """ + A newsfeed is a collection of news items, targeted to a specific audience. + + Newsfeeds currently cannot be edited through the API, please refer to [this article](https://www.intercom.com/help/en/articles/6362267-getting-started-with-news) to set up your newsfeeds in Intercom. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the newsfeed which is given by Intercom. + """ + + type: typing.Optional[typing.Literal["newsfeed"]] = pydantic.Field(default=None) + """ + The type of object. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the newsfeed. This name will never be visible to your users. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Timestamp for when the newsfeed was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Timestamp for when the newsfeed was last updated. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/news/types/newsfeed_assignment.py b/src/intercom/news/types/newsfeed_assignment.py new file mode 100644 index 00000000..087b6b85 --- /dev/null +++ b/src/intercom/news/types/newsfeed_assignment.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class NewsfeedAssignment(UncheckedBaseModel): + """ + Assigns a news item to a newsfeed. + """ + + newsfeed_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The unique identifier for the newsfeed which is given by Intercom. Publish dates cannot be in the future, to schedule news items use the dedicated feature in app (see this article). + """ + + published_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Publish date of the news item on the newsfeed, use this field if you want to set a publish date in the past (e.g. when importing existing news items). On write, this field will be ignored if the news item state is "draft". + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/notes/__init__.py b/src/intercom/notes/__init__.py new file mode 100644 index 00000000..862e9e5c --- /dev/null +++ b/src/intercom/notes/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import Note, NoteContact +_dynamic_imports: typing.Dict[str, str] = {"Note": ".types", "NoteContact": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Note", "NoteContact"] diff --git a/src/intercom/notes/client.py b/src/intercom/notes/client.py new file mode 100644 index 00000000..41dba3c0 --- /dev/null +++ b/src/intercom/notes/client.py @@ -0,0 +1,322 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.pagination import AsyncPager, SyncPager +from ..core.request_options import RequestOptions +from ..types.note_list import NoteList +from .raw_client import AsyncRawNotesClient, RawNotesClient +from .types.note import Note + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class NotesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawNotesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawNotesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawNotesClient + """ + return self._raw_client + + def list( + self, + contact_id: str, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Note, NoteList]: + """ + You can fetch a list of notes that are associated to a contact. + + Parameters + ---------- + contact_id : str + The unique identifier of a contact. + + 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[Note, NoteList] + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + response = client.notes.list( + contact_id="contact_id", + ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.list(contact_id, page=page, per_page=per_page, request_options=request_options) + + def create( + self, + contact_id: str, + *, + body: str, + admin_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Note: + """ + You can add a note to a single contact. + + Parameters + ---------- + contact_id : str + The unique identifier of a given contact. + + body : str + The text of the note. + + admin_id : typing.Optional[str] + The unique identifier of a given admin. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Note + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.notes.create( + contact_id="123", + body="Hello", + admin_id="123", + ) + """ + _response = self._raw_client.create(contact_id, body=body, admin_id=admin_id, request_options=request_options) + return _response.data + + def find(self, note_id: int, *, request_options: typing.Optional[RequestOptions] = None) -> Note: + """ + You can fetch the details of a single note. + + Parameters + ---------- + note_id : int + The unique identifier of a given note + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Note + Note found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.notes.find( + note_id=1, + ) + """ + _response = self._raw_client.find(note_id, request_options=request_options) + return _response.data + + +class AsyncNotesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawNotesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawNotesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawNotesClient + """ + return self._raw_client + + async def list( + self, + contact_id: str, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[Note, NoteList]: + """ + You can fetch a list of notes that are associated to a contact. + + Parameters + ---------- + contact_id : str + The unique identifier of a contact. + + 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[Note, NoteList] + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + response = await client.notes.list( + contact_id="contact_id", + ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page + + + asyncio.run(main()) + """ + return await self._raw_client.list(contact_id, page=page, per_page=per_page, request_options=request_options) + + async def create( + self, + contact_id: str, + *, + body: str, + admin_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Note: + """ + You can add a note to a single contact. + + Parameters + ---------- + contact_id : str + The unique identifier of a given contact. + + body : str + The text of the note. + + admin_id : typing.Optional[str] + The unique identifier of a given admin. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Note + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.notes.create( + contact_id="123", + body="Hello", + admin_id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + contact_id, body=body, admin_id=admin_id, request_options=request_options + ) + return _response.data + + async def find(self, note_id: int, *, request_options: typing.Optional[RequestOptions] = None) -> Note: + """ + You can fetch the details of a single note. + + Parameters + ---------- + note_id : int + The unique identifier of a given note + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Note + Note found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.notes.find( + note_id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.find(note_id, request_options=request_options) + return _response.data diff --git a/src/intercom/notes/raw_client.py b/src/intercom/notes/raw_client.py new file mode 100644 index 00000000..00e39d4e --- /dev/null +++ b/src/intercom/notes/raw_client.py @@ -0,0 +1,441 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.unchecked_base_model import construct_type +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from ..types.note_list import NoteList +from .types.note import Note + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawNotesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list( + self, + contact_id: str, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Note, NoteList]: + """ + You can fetch a list of notes that are associated to a contact. + + Parameters + ---------- + contact_id : str + The unique identifier of a contact. + + 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[Note, NoteList] + Successful response + """ + page = page if page is not None else 1 + + _response = self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(contact_id)}/notes", + method="GET", + params={ + "page": page, + "per_page": per_page, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + NoteList, + construct_type( + type_=NoteList, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.data + _has_next = True + _get_next = lambda: self.list( + 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 == 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 create( + self, + contact_id: str, + *, + body: str, + admin_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Note]: + """ + You can add a note to a single contact. + + Parameters + ---------- + contact_id : str + The unique identifier of a given contact. + + body : str + The text of the note. + + admin_id : typing.Optional[str] + The unique identifier of a given admin. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Note] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(contact_id)}/notes", + method="POST", + json={ + "body": body, + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Note, + construct_type( + type_=Note, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + 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, note_id: int, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Note]: + """ + You can fetch the details of a single note. + + Parameters + ---------- + note_id : int + The unique identifier of a given note + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Note] + Note found + """ + _response = self._client_wrapper.httpx_client.request( + f"notes/{jsonable_encoder(note_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Note, + construct_type( + type_=Note, # 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) + + +class AsyncRawNotesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, + contact_id: str, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[Note, NoteList]: + """ + You can fetch a list of notes that are associated to a contact. + + Parameters + ---------- + contact_id : str + The unique identifier of a contact. + + 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[Note, NoteList] + Successful response + """ + page = page if page is not None else 1 + + _response = await self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(contact_id)}/notes", + method="GET", + params={ + "page": page, + "per_page": per_page, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + NoteList, + construct_type( + type_=NoteList, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.data + _has_next = True + + async def _get_next(): + return await self.list( + 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 == 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 create( + self, + contact_id: str, + *, + body: str, + admin_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Note]: + """ + You can add a note to a single contact. + + Parameters + ---------- + contact_id : str + The unique identifier of a given contact. + + body : str + The text of the note. + + admin_id : typing.Optional[str] + The unique identifier of a given admin. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Note] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(contact_id)}/notes", + method="POST", + json={ + "body": body, + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Note, + construct_type( + type_=Note, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + 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, note_id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Note]: + """ + You can fetch the details of a single note. + + Parameters + ---------- + note_id : int + The unique identifier of a given note + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Note] + Note found + """ + _response = await self._client_wrapper.httpx_client.request( + f"notes/{jsonable_encoder(note_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Note, + construct_type( + type_=Note, # 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) diff --git a/src/intercom/notes/types/__init__.py b/src/intercom/notes/types/__init__.py new file mode 100644 index 00000000..1d17f30f --- /dev/null +++ b/src/intercom/notes/types/__init__.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .note import Note + from .note_contact import NoteContact +_dynamic_imports: typing.Dict[str, str] = {"Note": ".note", "NoteContact": ".note_contact"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Note", "NoteContact"] diff --git a/src/intercom/notes/types/note.py b/src/intercom/notes/types/note.py new file mode 100644 index 00000000..3bddbc5f --- /dev/null +++ b/src/intercom/notes/types/note.py @@ -0,0 +1,54 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...admins.types.admin import Admin +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .note_contact import NoteContact + + +class Note(UncheckedBaseModel): + """ + Notes allow you to annotate and comment on your contacts. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `note`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the note. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the note was created. + """ + + contact: typing.Optional[NoteContact] = pydantic.Field(default=None) + """ + Represents the contact that the note was created about. + """ + + author: typing.Optional[Admin] = pydantic.Field(default=None) + """ + Optional. Represents the Admin that created the note. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The body text of the note. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/notes/types/note_contact.py b/src/intercom/notes/types/note_contact.py new file mode 100644 index 00000000..35a73e5b --- /dev/null +++ b/src/intercom/notes/types/note_contact.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class NoteContact(UncheckedBaseModel): + """ + Represents the contact that the note was created about. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `contact`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the contact. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/phone_call_redirects/__init__.py b/src/intercom/phone_call_redirects/__init__.py new file mode 100644 index 00000000..5cde0202 --- /dev/null +++ b/src/intercom/phone_call_redirects/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/src/intercom/phone_call_redirects/client.py b/src/intercom/phone_call_redirects/client.py new file mode 100644 index 00000000..b5e4108d --- /dev/null +++ b/src/intercom/phone_call_redirects/client.py @@ -0,0 +1,134 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.create_phone_switch_request import CreatePhoneSwitchRequest +from ..types.phone_switch import PhoneSwitch +from .raw_client import AsyncRawPhoneCallRedirectsClient, RawPhoneCallRedirectsClient + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class PhoneCallRedirectsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawPhoneCallRedirectsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawPhoneCallRedirectsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawPhoneCallRedirectsClient + """ + return self._raw_client + + def create( + self, + *, + request: typing.Optional[CreatePhoneSwitchRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[PhoneSwitch]: + """ + You can use the API to deflect phone calls to the Intercom Messenger. + Calling this endpoint will send an SMS with a link to the Messenger to the phone number specified. + + If custom attributes are specified, they will be added to the user or lead's custom data attributes. + + Parameters + ---------- + request : typing.Optional[CreatePhoneSwitchRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[PhoneSwitch] + successful + + Examples + -------- + from intercom import CreatePhoneSwitchRequest, Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.phone_call_redirects.create( + request=CreatePhoneSwitchRequest( + phone="+353832345678", + custom_attributes={"issue_type": "Billing", "priority": "High"}, + ), + ) + """ + _response = self._raw_client.create(request=request, request_options=request_options) + return _response.data + + +class AsyncPhoneCallRedirectsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawPhoneCallRedirectsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawPhoneCallRedirectsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawPhoneCallRedirectsClient + """ + return self._raw_client + + async def create( + self, + *, + request: typing.Optional[CreatePhoneSwitchRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[PhoneSwitch]: + """ + You can use the API to deflect phone calls to the Intercom Messenger. + Calling this endpoint will send an SMS with a link to the Messenger to the phone number specified. + + If custom attributes are specified, they will be added to the user or lead's custom data attributes. + + Parameters + ---------- + request : typing.Optional[CreatePhoneSwitchRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[PhoneSwitch] + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom, CreatePhoneSwitchRequest + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.phone_call_redirects.create( + request=CreatePhoneSwitchRequest( + phone="+353832345678", + custom_attributes={"issue_type": "Billing", "priority": "High"}, + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create(request=request, request_options=request_options) + return _response.data diff --git a/src/intercom/phone_call_redirects/raw_client.py b/src/intercom/phone_call_redirects/raw_client.py new file mode 100644 index 00000000..7427f86c --- /dev/null +++ b/src/intercom/phone_call_redirects/raw_client.py @@ -0,0 +1,202 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.http_response import AsyncHttpResponse, HttpResponse +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.bad_request_error import BadRequestError +from ..errors.unauthorized_error import UnauthorizedError +from ..errors.unprocessable_entity_error import UnprocessableEntityError +from ..types.create_phone_switch_request import CreatePhoneSwitchRequest +from ..types.error import Error +from ..types.phone_switch import PhoneSwitch + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawPhoneCallRedirectsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def create( + self, + *, + request: typing.Optional[CreatePhoneSwitchRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[typing.Optional[PhoneSwitch]]: + """ + You can use the API to deflect phone calls to the Intercom Messenger. + Calling this endpoint will send an SMS with a link to the Messenger to the phone number specified. + + If custom attributes are specified, they will be added to the user or lead's custom data attributes. + + Parameters + ---------- + request : typing.Optional[CreatePhoneSwitchRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[PhoneSwitch]] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "phone_call_redirects", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreatePhoneSwitchRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[PhoneSwitch], + construct_type( + type_=typing.Optional[PhoneSwitch], # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 == 422: + raise UnprocessableEntityError( + 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) + + +class AsyncRawPhoneCallRedirectsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def create( + self, + *, + request: typing.Optional[CreatePhoneSwitchRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[typing.Optional[PhoneSwitch]]: + """ + You can use the API to deflect phone calls to the Intercom Messenger. + Calling this endpoint will send an SMS with a link to the Messenger to the phone number specified. + + If custom attributes are specified, they will be added to the user or lead's custom data attributes. + + Parameters + ---------- + request : typing.Optional[CreatePhoneSwitchRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[PhoneSwitch]] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "phone_call_redirects", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreatePhoneSwitchRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[PhoneSwitch], + construct_type( + type_=typing.Optional[PhoneSwitch], # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 == 422: + raise UnprocessableEntityError( + 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) diff --git a/src/intercom/py.typed b/src/intercom/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/src/intercom/segments/__init__.py b/src/intercom/segments/__init__.py new file mode 100644 index 00000000..d5f7ec59 --- /dev/null +++ b/src/intercom/segments/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import Segment, SegmentPersonType +_dynamic_imports: typing.Dict[str, str] = {"Segment": ".types", "SegmentPersonType": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Segment", "SegmentPersonType"] diff --git a/src/intercom/segments/client.py b/src/intercom/segments/client.py new file mode 100644 index 00000000..c448d94d --- /dev/null +++ b/src/intercom/segments/client.py @@ -0,0 +1,185 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.segment_list import SegmentList +from .raw_client import AsyncRawSegmentsClient, RawSegmentsClient +from .types.segment import Segment + + +class SegmentsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawSegmentsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawSegmentsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawSegmentsClient + """ + return self._raw_client + + def list( + self, *, include_count: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None + ) -> SegmentList: + """ + You can fetch a list of all segments. + + Parameters + ---------- + include_count : typing.Optional[bool] + It includes the count of contacts that belong to each segment. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SegmentList + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.segments.list( + include_count=True, + ) + """ + _response = self._raw_client.list(include_count=include_count, request_options=request_options) + return _response.data + + def find(self, segment_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Segment: + """ + You can fetch the details of a single segment. + + Parameters + ---------- + segment_id : str + The unique identified of a given segment. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Segment + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.segments.find( + segment_id="123", + ) + """ + _response = self._raw_client.find(segment_id, request_options=request_options) + return _response.data + + +class AsyncSegmentsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawSegmentsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawSegmentsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawSegmentsClient + """ + return self._raw_client + + async def list( + self, *, include_count: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None + ) -> SegmentList: + """ + You can fetch a list of all segments. + + Parameters + ---------- + include_count : typing.Optional[bool] + It includes the count of contacts that belong to each segment. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SegmentList + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.segments.list( + include_count=True, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list(include_count=include_count, request_options=request_options) + return _response.data + + async def find(self, segment_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Segment: + """ + You can fetch the details of a single segment. + + Parameters + ---------- + segment_id : str + The unique identified of a given segment. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Segment + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.segments.find( + segment_id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.find(segment_id, request_options=request_options) + return _response.data diff --git a/src/intercom/segments/raw_client.py b/src/intercom/segments/raw_client.py new file mode 100644 index 00000000..5bec1f00 --- /dev/null +++ b/src/intercom/segments/raw_client.py @@ -0,0 +1,254 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ..core.unchecked_base_model import construct_type +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from ..types.segment_list import SegmentList +from .types.segment import Segment + + +class RawSegmentsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list( + self, *, include_count: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[SegmentList]: + """ + You can fetch a list of all segments. + + Parameters + ---------- + include_count : typing.Optional[bool] + It includes the count of contacts that belong to each segment. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[SegmentList] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "segments", + method="GET", + params={ + "include_count": include_count, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SegmentList, + construct_type( + type_=SegmentList, # 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 find( + self, segment_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Segment]: + """ + You can fetch the details of a single segment. + + Parameters + ---------- + segment_id : str + The unique identified of a given segment. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Segment] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + f"segments/{jsonable_encoder(segment_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Segment, + construct_type( + type_=Segment, # 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) + + +class AsyncRawSegmentsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, *, include_count: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[SegmentList]: + """ + You can fetch a list of all segments. + + Parameters + ---------- + include_count : typing.Optional[bool] + It includes the count of contacts that belong to each segment. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[SegmentList] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "segments", + method="GET", + params={ + "include_count": include_count, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SegmentList, + construct_type( + type_=SegmentList, # 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 find( + self, segment_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Segment]: + """ + You can fetch the details of a single segment. + + Parameters + ---------- + segment_id : str + The unique identified of a given segment. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Segment] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + f"segments/{jsonable_encoder(segment_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Segment, + construct_type( + type_=Segment, # 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) diff --git a/src/intercom/segments/types/__init__.py b/src/intercom/segments/types/__init__.py new file mode 100644 index 00000000..c6ec8262 --- /dev/null +++ b/src/intercom/segments/types/__init__.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .segment import Segment + from .segment_person_type import SegmentPersonType +_dynamic_imports: typing.Dict[str, str] = {"Segment": ".segment", "SegmentPersonType": ".segment_person_type"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Segment", "SegmentPersonType"] diff --git a/src/intercom/segments/types/segment.py b/src/intercom/segments/types/segment.py new file mode 100644 index 00000000..b3851984 --- /dev/null +++ b/src/intercom/segments/types/segment.py @@ -0,0 +1,58 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .segment_person_type import SegmentPersonType + + +class Segment(UncheckedBaseModel): + """ + A segment is a group of your contacts defined by the rules that you set. + """ + + type: typing.Optional[typing.Literal["segment"]] = pydantic.Field(default=None) + """ + The type of object. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier representing the segment. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the segment. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the segment was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the segment was updated. + """ + + person_type: typing.Optional[SegmentPersonType] = pydantic.Field(default=None) + """ + Type of the contact: contact (lead) or user. + """ + + count: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of items in the user segment. It's returned when `include_count=true` is included in the request. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/segments/types/segment_person_type.py b/src/intercom/segments/types/segment_person_type.py new file mode 100644 index 00000000..3089d52f --- /dev/null +++ b/src/intercom/segments/types/segment_person_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +SegmentPersonType = typing.Union[typing.Literal["contact", "user"], typing.Any] diff --git a/src/intercom/subscription_types/__init__.py b/src/intercom/subscription_types/__init__.py new file mode 100644 index 00000000..306f22bf --- /dev/null +++ b/src/intercom/subscription_types/__init__.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + SubscriptionType, + SubscriptionTypeConsentType, + SubscriptionTypeContentTypesItem, + SubscriptionTypeState, + ) +_dynamic_imports: typing.Dict[str, str] = { + "SubscriptionType": ".types", + "SubscriptionTypeConsentType": ".types", + "SubscriptionTypeContentTypesItem": ".types", + "SubscriptionTypeState": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "SubscriptionType", + "SubscriptionTypeConsentType", + "SubscriptionTypeContentTypesItem", + "SubscriptionTypeState", +] diff --git a/src/intercom/subscription_types/client.py b/src/intercom/subscription_types/client.py new file mode 100644 index 00000000..dc7367cf --- /dev/null +++ b/src/intercom/subscription_types/client.py @@ -0,0 +1,100 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.subscription_type_list import SubscriptionTypeList +from .raw_client import AsyncRawSubscriptionTypesClient, RawSubscriptionTypesClient + + +class SubscriptionTypesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawSubscriptionTypesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawSubscriptionTypesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawSubscriptionTypesClient + """ + return self._raw_client + + def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> SubscriptionTypeList: + """ + You can list all subscription types. A list of subscription type objects will be returned. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SubscriptionTypeList + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.subscription_types.list() + """ + _response = self._raw_client.list(request_options=request_options) + return _response.data + + +class AsyncSubscriptionTypesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawSubscriptionTypesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawSubscriptionTypesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawSubscriptionTypesClient + """ + return self._raw_client + + async def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> SubscriptionTypeList: + """ + You can list all subscription types. A list of subscription type objects will be returned. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SubscriptionTypeList + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.subscription_types.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list(request_options=request_options) + return _response.data diff --git a/src/intercom/subscription_types/raw_client.py b/src/intercom/subscription_types/raw_client.py new file mode 100644 index 00000000..99018bfd --- /dev/null +++ b/src/intercom/subscription_types/raw_client.py @@ -0,0 +1,115 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.request_options import RequestOptions +from ..core.unchecked_base_model import construct_type +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from ..types.subscription_type_list import SubscriptionTypeList + + +class RawSubscriptionTypesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[SubscriptionTypeList]: + """ + You can list all subscription types. A list of subscription type objects will be returned. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[SubscriptionTypeList] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + "subscription_types", + 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(), + ), + ), + ) + _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 AsyncRawSubscriptionTypesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[SubscriptionTypeList]: + """ + You can list all subscription types. A list of subscription type objects will be returned. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[SubscriptionTypeList] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + "subscription_types", + 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(), + ), + ), + ) + _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) diff --git a/src/intercom/subscription_types/types/__init__.py b/src/intercom/subscription_types/types/__init__.py new file mode 100644 index 00000000..dfc1b4cb --- /dev/null +++ b/src/intercom/subscription_types/types/__init__.py @@ -0,0 +1,47 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .subscription_type import SubscriptionType + from .subscription_type_consent_type import SubscriptionTypeConsentType + from .subscription_type_content_types_item import SubscriptionTypeContentTypesItem + from .subscription_type_state import SubscriptionTypeState +_dynamic_imports: typing.Dict[str, str] = { + "SubscriptionType": ".subscription_type", + "SubscriptionTypeConsentType": ".subscription_type_consent_type", + "SubscriptionTypeContentTypesItem": ".subscription_type_content_types_item", + "SubscriptionTypeState": ".subscription_type_state", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "SubscriptionType", + "SubscriptionTypeConsentType", + "SubscriptionTypeContentTypesItem", + "SubscriptionTypeState", +] diff --git a/src/intercom/subscription_types/types/subscription_type.py b/src/intercom/subscription_types/types/subscription_type.py new file mode 100644 index 00000000..8f4c0f2a --- /dev/null +++ b/src/intercom/subscription_types/types/subscription_type.py @@ -0,0 +1,57 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.translation import Translation +from .subscription_type_consent_type import SubscriptionTypeConsentType +from .subscription_type_content_types_item import SubscriptionTypeContentTypesItem +from .subscription_type_state import SubscriptionTypeState + + +class SubscriptionType(UncheckedBaseModel): + """ + A subscription type lets customers easily opt out of non-essential communications without missing what's important to them. + """ + + type: typing.Optional[typing.Literal["subscription"]] = pydantic.Field(default=None) + """ + The type of the object - subscription + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier representing the subscription type. + """ + + state: typing.Optional[SubscriptionTypeState] = pydantic.Field(default=None) + """ + The state of the subscription type. + """ + + default_translation: typing.Optional[Translation] = None + translations: typing.Optional[typing.List[Translation]] = pydantic.Field(default=None) + """ + An array of translations objects with the localised version of the subscription type in each available locale within your translation settings. + """ + + consent_type: typing.Optional[SubscriptionTypeConsentType] = pydantic.Field(default=None) + """ + Describes the type of consent. + """ + + content_types: typing.Optional[typing.List[SubscriptionTypeContentTypesItem]] = pydantic.Field(default=None) + """ + The message types that this subscription supports - can contain `email` or `sms_message`. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/subscription_types/types/subscription_type_consent_type.py b/src/intercom/subscription_types/types/subscription_type_consent_type.py new file mode 100644 index 00000000..d7f8b6a9 --- /dev/null +++ b/src/intercom/subscription_types/types/subscription_type_consent_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +SubscriptionTypeConsentType = typing.Union[typing.Literal["opt_out", "opt_in"], typing.Any] diff --git a/src/intercom/subscription_types/types/subscription_type_content_types_item.py b/src/intercom/subscription_types/types/subscription_type_content_types_item.py new file mode 100644 index 00000000..d895f703 --- /dev/null +++ b/src/intercom/subscription_types/types/subscription_type_content_types_item.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +SubscriptionTypeContentTypesItem = typing.Union[typing.Literal["email", "sms_message"], typing.Any] diff --git a/src/intercom/subscription_types/types/subscription_type_state.py b/src/intercom/subscription_types/types/subscription_type_state.py new file mode 100644 index 00000000..65c92596 --- /dev/null +++ b/src/intercom/subscription_types/types/subscription_type_state.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +SubscriptionTypeState = typing.Union[typing.Literal["live", "draft", "archived"], typing.Any] diff --git a/src/intercom/tags/__init__.py b/src/intercom/tags/__init__.py new file mode 100644 index 00000000..9d4e7f95 --- /dev/null +++ b/src/intercom/tags/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import Tag, TagBasic, TagsCreateRequestBody +_dynamic_imports: typing.Dict[str, str] = {"Tag": ".types", "TagBasic": ".types", "TagsCreateRequestBody": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Tag", "TagBasic", "TagsCreateRequestBody"] diff --git a/src/intercom/tags/client.py b/src/intercom/tags/client.py new file mode 100644 index 00000000..fdb545d8 --- /dev/null +++ b/src/intercom/tags/client.py @@ -0,0 +1,901 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.tag_list import TagList +from .raw_client import AsyncRawTagsClient, RawTagsClient +from .types.tag import Tag +from .types.tags_create_request_body import TagsCreateRequestBody + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class TagsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawTagsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawTagsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawTagsClient + """ + return self._raw_client + + def tag_contact( + self, contact_id: str, *, tag_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can tag a specific contact. This will return a tag object for the tag that was added to the contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + tag_id : str + The unique identifier for the tag which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.tags.tag_contact( + contact_id="63a07ddf05a32042dffac965", + tag_id="7522907", + ) + """ + _response = self._raw_client.tag_contact(contact_id, tag_id=tag_id, request_options=request_options) + return _response.data + + def untag_contact( + self, contact_id: str, tag_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can remove tag from a specific contact. This will return a tag object for the tag that was removed from the contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + tag_id : str + The unique identifier for the tag which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.tags.untag_contact( + contact_id="63a07ddf05a32042dffac965", + tag_id="7522907", + ) + """ + _response = self._raw_client.untag_contact(contact_id, tag_id, request_options=request_options) + return _response.data + + def tag_conversation( + self, + conversation_id: str, + *, + tag_id: str, + admin_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> Tag: + """ + You can tag a specific conversation. This will return a tag object for the tag that was added to the conversation. + + Parameters + ---------- + conversation_id : str + conversation_id + + tag_id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.tags.tag_conversation( + conversation_id="64619700005694", + tag_id="7522907", + admin_id="780", + ) + """ + _response = self._raw_client.tag_conversation( + conversation_id, tag_id=tag_id, admin_id=admin_id, request_options=request_options + ) + return _response.data + + def untag_conversation( + self, + conversation_id: str, + tag_id: str, + *, + admin_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> Tag: + """ + You can remove tag from a specific conversation. This will return a tag object for the tag that was removed from the conversation. + + Parameters + ---------- + conversation_id : str + conversation_id + + tag_id : str + tag_id + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.tags.untag_conversation( + conversation_id="64619700005694", + tag_id="7522907", + admin_id="123", + ) + """ + _response = self._raw_client.untag_conversation( + conversation_id, tag_id, admin_id=admin_id, request_options=request_options + ) + return _response.data + + def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> TagList: + """ + You can fetch a list of all tags for a given workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TagList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.tags.list() + """ + _response = self._raw_client.list(request_options=request_options) + return _response.data + + def create(self, *, request: TagsCreateRequestBody, request_options: typing.Optional[RequestOptions] = None) -> Tag: + """ + You can use this endpoint to perform the following operations: + + **1. Create a new tag:** You can create a new tag by passing in the tag name as specified in "Create or Update Tag Request Payload" described below. + + **2. Update an existing tag:** You can update an existing tag by passing the id of the tag as specified in "Create or Update Tag Request Payload" described below. + + **3. Tag Companies:** You can tag single company or a list of companies. You can tag a company by passing in the tag name and the company details as specified in "Tag Company Request Payload" described below. Also, if the tag doesn't exist then a new one will be created automatically. + + **4. Untag Companies:** You can untag a single company or a list of companies. You can untag a company by passing in the tag id and the company details as specified in "Untag Company Request Payload" described below. + + **5. Tag Multiple Users:** You can tag a list of users. You can tag the users by passing in the tag name and the user details as specified in "Tag Users Request Payload" described below. + + Each operation will return a tag object. + + Parameters + ---------- + request : TagsCreateRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + Action successful + + Examples + -------- + from intercom import CreateOrUpdateTagRequest, Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.tags.create( + request=CreateOrUpdateTagRequest( + name="test", + ), + ) + """ + _response = self._raw_client.create(request=request, request_options=request_options) + return _response.data + + def find(self, tag_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Tag: + """ + You can fetch the details of tags that are on the workspace by their id. + This will return a tag object. + + Parameters + ---------- + tag_id : str + The unique identifier of a given tag + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + Tag found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.tags.find( + tag_id="123", + ) + """ + _response = self._raw_client.find(tag_id, request_options=request_options) + return _response.data + + def delete(self, tag_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + You can delete the details of tags that are on the workspace by passing in the id. + + Parameters + ---------- + tag_id : str + The unique identifier of a given tag + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.tags.delete( + tag_id="123", + ) + """ + _response = self._raw_client.delete(tag_id, request_options=request_options) + return _response.data + + def tag_ticket( + self, ticket_id: str, *, tag_id: str, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can tag a specific ticket. This will return a tag object for the tag that was added to the ticket. + + Parameters + ---------- + ticket_id : str + ticket_id + + tag_id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.tags.tag_ticket( + ticket_id="64619700005694", + tag_id="7522907", + admin_id="780", + ) + """ + _response = self._raw_client.tag_ticket( + ticket_id, tag_id=tag_id, admin_id=admin_id, request_options=request_options + ) + return _response.data + + def untag_ticket( + self, ticket_id: str, tag_id: str, *, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can remove tag from a specific ticket. This will return a tag object for the tag that was removed from the ticket. + + Parameters + ---------- + ticket_id : str + ticket_id + + tag_id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.tags.untag_ticket( + ticket_id="64619700005694", + tag_id="7522907", + admin_id="123", + ) + """ + _response = self._raw_client.untag_ticket(ticket_id, tag_id, admin_id=admin_id, request_options=request_options) + return _response.data + + +class AsyncTagsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawTagsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawTagsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawTagsClient + """ + return self._raw_client + + async def tag_contact( + self, contact_id: str, *, tag_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can tag a specific contact. This will return a tag object for the tag that was added to the contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + tag_id : str + The unique identifier for the tag which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.tags.tag_contact( + contact_id="63a07ddf05a32042dffac965", + tag_id="7522907", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.tag_contact(contact_id, tag_id=tag_id, request_options=request_options) + return _response.data + + async def untag_contact( + self, contact_id: str, tag_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can remove tag from a specific contact. This will return a tag object for the tag that was removed from the contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + tag_id : str + The unique identifier for the tag which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.tags.untag_contact( + contact_id="63a07ddf05a32042dffac965", + tag_id="7522907", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.untag_contact(contact_id, tag_id, request_options=request_options) + return _response.data + + async def tag_conversation( + self, + conversation_id: str, + *, + tag_id: str, + admin_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> Tag: + """ + You can tag a specific conversation. This will return a tag object for the tag that was added to the conversation. + + Parameters + ---------- + conversation_id : str + conversation_id + + tag_id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.tags.tag_conversation( + conversation_id="64619700005694", + tag_id="7522907", + admin_id="780", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.tag_conversation( + conversation_id, tag_id=tag_id, admin_id=admin_id, request_options=request_options + ) + return _response.data + + async def untag_conversation( + self, + conversation_id: str, + tag_id: str, + *, + admin_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> Tag: + """ + You can remove tag from a specific conversation. This will return a tag object for the tag that was removed from the conversation. + + Parameters + ---------- + conversation_id : str + conversation_id + + tag_id : str + tag_id + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.tags.untag_conversation( + conversation_id="64619700005694", + tag_id="7522907", + admin_id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.untag_conversation( + conversation_id, tag_id, admin_id=admin_id, request_options=request_options + ) + return _response.data + + async def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> TagList: + """ + You can fetch a list of all tags for a given workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TagList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.tags.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list(request_options=request_options) + return _response.data + + async def create( + self, *, request: TagsCreateRequestBody, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can use this endpoint to perform the following operations: + + **1. Create a new tag:** You can create a new tag by passing in the tag name as specified in "Create or Update Tag Request Payload" described below. + + **2. Update an existing tag:** You can update an existing tag by passing the id of the tag as specified in "Create or Update Tag Request Payload" described below. + + **3. Tag Companies:** You can tag single company or a list of companies. You can tag a company by passing in the tag name and the company details as specified in "Tag Company Request Payload" described below. Also, if the tag doesn't exist then a new one will be created automatically. + + **4. Untag Companies:** You can untag a single company or a list of companies. You can untag a company by passing in the tag id and the company details as specified in "Untag Company Request Payload" described below. + + **5. Tag Multiple Users:** You can tag a list of users. You can tag the users by passing in the tag name and the user details as specified in "Tag Users Request Payload" described below. + + Each operation will return a tag object. + + Parameters + ---------- + request : TagsCreateRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + Action successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom, CreateOrUpdateTagRequest + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.tags.create( + request=CreateOrUpdateTagRequest( + name="test", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create(request=request, request_options=request_options) + return _response.data + + async def find(self, tag_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Tag: + """ + You can fetch the details of tags that are on the workspace by their id. + This will return a tag object. + + Parameters + ---------- + tag_id : str + The unique identifier of a given tag + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + Tag found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.tags.find( + tag_id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.find(tag_id, request_options=request_options) + return _response.data + + async def delete(self, tag_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + You can delete the details of tags that are on the workspace by passing in the id. + + Parameters + ---------- + tag_id : str + The unique identifier of a given tag + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.tags.delete( + tag_id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete(tag_id, request_options=request_options) + return _response.data + + async def tag_ticket( + self, ticket_id: str, *, tag_id: str, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can tag a specific ticket. This will return a tag object for the tag that was added to the ticket. + + Parameters + ---------- + ticket_id : str + ticket_id + + tag_id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.tags.tag_ticket( + ticket_id="64619700005694", + tag_id="7522907", + admin_id="780", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.tag_ticket( + ticket_id, tag_id=tag_id, admin_id=admin_id, request_options=request_options + ) + return _response.data + + async def untag_ticket( + self, ticket_id: str, tag_id: str, *, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can remove tag from a specific ticket. This will return a tag object for the tag that was removed from the ticket. + + Parameters + ---------- + ticket_id : str + ticket_id + + tag_id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.tags.untag_ticket( + ticket_id="64619700005694", + tag_id="7522907", + admin_id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.untag_ticket( + ticket_id, tag_id, admin_id=admin_id, request_options=request_options + ) + return _response.data diff --git a/src/intercom/tags/raw_client.py b/src/intercom/tags/raw_client.py new file mode 100644 index 00000000..16368b2c --- /dev/null +++ b/src/intercom/tags/raw_client.py @@ -0,0 +1,1436 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ..core.serialization import convert_and_respect_annotation_metadata +from ..core.unchecked_base_model import construct_type +from ..errors.bad_request_error import BadRequestError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from ..types.tag_list import TagList +from .types.tag import Tag +from .types.tags_create_request_body import TagsCreateRequestBody + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawTagsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def tag_contact( + self, contact_id: str, *, tag_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Tag]: + """ + You can tag a specific contact. This will return a tag object for the tag that was added to the contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + tag_id : str + The unique identifier for the tag which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Tag] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(contact_id)}/tags", + method="POST", + json={ + "id": tag_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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 untag_contact( + self, contact_id: str, tag_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Tag]: + """ + You can remove tag from a specific contact. This will return a tag object for the tag that was removed from the contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + tag_id : str + The unique identifier for the tag which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Tag] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(contact_id)}/tags/{jsonable_encoder(tag_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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 tag_conversation( + self, + conversation_id: str, + *, + tag_id: str, + admin_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Tag]: + """ + You can tag a specific conversation. This will return a tag object for the tag that was added to the conversation. + + Parameters + ---------- + conversation_id : str + conversation_id + + tag_id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Tag] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/tags", + method="POST", + json={ + "id": tag_id, + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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 untag_conversation( + self, + conversation_id: str, + tag_id: str, + *, + admin_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Tag]: + """ + You can remove tag from a specific conversation. This will return a tag object for the tag that was removed from the conversation. + + Parameters + ---------- + conversation_id : str + conversation_id + + tag_id : str + tag_id + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Tag] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/tags/{jsonable_encoder(tag_id)}", + method="DELETE", + json={ + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[TagList]: + """ + You can fetch a list of all tags for a given workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[TagList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "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(), + ), + ), + ) + _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: TagsCreateRequestBody, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Tag]: + """ + You can use this endpoint to perform the following operations: + + **1. Create a new tag:** You can create a new tag by passing in the tag name as specified in "Create or Update Tag Request Payload" described below. + + **2. Update an existing tag:** You can update an existing tag by passing the id of the tag as specified in "Create or Update Tag Request Payload" described below. + + **3. Tag Companies:** You can tag single company or a list of companies. You can tag a company by passing in the tag name and the company details as specified in "Tag Company Request Payload" described below. Also, if the tag doesn't exist then a new one will be created automatically. + + **4. Untag Companies:** You can untag a single company or a list of companies. You can untag a company by passing in the tag id and the company details as specified in "Untag Company Request Payload" described below. + + **5. Tag Multiple Users:** You can tag a list of users. You can tag the users by passing in the tag name and the user details as specified in "Tag Users Request Payload" described below. + + Each operation will return a tag object. + + Parameters + ---------- + request : TagsCreateRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Tag] + Action successful + """ + _response = self._client_wrapper.httpx_client.request( + "tags", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=TagsCreateRequestBody, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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, tag_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Tag]: + """ + You can fetch the details of tags that are on the workspace by their id. + This will return a tag object. + + Parameters + ---------- + tag_id : str + The unique identifier of a given tag + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Tag] + Tag found + """ + _response = self._client_wrapper.httpx_client.request( + f"tags/{jsonable_encoder(tag_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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 delete(self, tag_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[None]: + """ + You can delete the details of tags that are on the workspace by passing in the id. + + Parameters + ---------- + tag_id : str + The unique identifier of a given tag + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + f"tags/{jsonable_encoder(tag_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 tag_ticket( + self, ticket_id: str, *, tag_id: str, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Tag]: + """ + You can tag a specific ticket. This will return a tag object for the tag that was added to the ticket. + + Parameters + ---------- + ticket_id : str + ticket_id + + tag_id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Tag] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(ticket_id)}/tags", + method="POST", + json={ + "id": tag_id, + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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 untag_ticket( + self, ticket_id: str, tag_id: str, *, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Tag]: + """ + You can remove tag from a specific ticket. This will return a tag object for the tag that was removed from the ticket. + + Parameters + ---------- + ticket_id : str + ticket_id + + tag_id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Tag] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(ticket_id)}/tags/{jsonable_encoder(tag_id)}", + method="DELETE", + json={ + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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) + + +class AsyncRawTagsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def tag_contact( + self, contact_id: str, *, tag_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Tag]: + """ + You can tag a specific contact. This will return a tag object for the tag that was added to the contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + tag_id : str + The unique identifier for the tag which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Tag] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(contact_id)}/tags", + method="POST", + json={ + "id": tag_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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 untag_contact( + self, contact_id: str, tag_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Tag]: + """ + You can remove tag from a specific contact. This will return a tag object for the tag that was removed from the contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + tag_id : str + The unique identifier for the tag which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Tag] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(contact_id)}/tags/{jsonable_encoder(tag_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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 tag_conversation( + self, + conversation_id: str, + *, + tag_id: str, + admin_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Tag]: + """ + You can tag a specific conversation. This will return a tag object for the tag that was added to the conversation. + + Parameters + ---------- + conversation_id : str + conversation_id + + tag_id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Tag] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/tags", + method="POST", + json={ + "id": tag_id, + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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 untag_conversation( + self, + conversation_id: str, + tag_id: str, + *, + admin_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Tag]: + """ + You can remove tag from a specific conversation. This will return a tag object for the tag that was removed from the conversation. + + Parameters + ---------- + conversation_id : str + conversation_id + + tag_id : str + tag_id + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Tag] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/tags/{jsonable_encoder(tag_id)}", + method="DELETE", + json={ + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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(self, *, request_options: typing.Optional[RequestOptions] = None) -> AsyncHttpResponse[TagList]: + """ + You can fetch a list of all tags for a given workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[TagList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "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(), + ), + ), + ) + _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: TagsCreateRequestBody, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Tag]: + """ + You can use this endpoint to perform the following operations: + + **1. Create a new tag:** You can create a new tag by passing in the tag name as specified in "Create or Update Tag Request Payload" described below. + + **2. Update an existing tag:** You can update an existing tag by passing the id of the tag as specified in "Create or Update Tag Request Payload" described below. + + **3. Tag Companies:** You can tag single company or a list of companies. You can tag a company by passing in the tag name and the company details as specified in "Tag Company Request Payload" described below. Also, if the tag doesn't exist then a new one will be created automatically. + + **4. Untag Companies:** You can untag a single company or a list of companies. You can untag a company by passing in the tag id and the company details as specified in "Untag Company Request Payload" described below. + + **5. Tag Multiple Users:** You can tag a list of users. You can tag the users by passing in the tag name and the user details as specified in "Tag Users Request Payload" described below. + + Each operation will return a tag object. + + Parameters + ---------- + request : TagsCreateRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Tag] + Action successful + """ + _response = await self._client_wrapper.httpx_client.request( + "tags", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=TagsCreateRequestBody, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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, tag_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Tag]: + """ + You can fetch the details of tags that are on the workspace by their id. + This will return a tag object. + + Parameters + ---------- + tag_id : str + The unique identifier of a given tag + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Tag] + Tag found + """ + _response = await self._client_wrapper.httpx_client.request( + f"tags/{jsonable_encoder(tag_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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 delete( + self, tag_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + You can delete the details of tags that are on the workspace by passing in the id. + + Parameters + ---------- + tag_id : str + The unique identifier of a given tag + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + f"tags/{jsonable_encoder(tag_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 tag_ticket( + self, ticket_id: str, *, tag_id: str, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Tag]: + """ + You can tag a specific ticket. This will return a tag object for the tag that was added to the ticket. + + Parameters + ---------- + ticket_id : str + ticket_id + + tag_id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Tag] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(ticket_id)}/tags", + method="POST", + json={ + "id": tag_id, + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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 untag_ticket( + self, ticket_id: str, tag_id: str, *, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Tag]: + """ + You can remove tag from a specific ticket. This will return a tag object for the tag that was removed from the ticket. + + Parameters + ---------- + ticket_id : str + ticket_id + + tag_id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Tag] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(ticket_id)}/tags/{jsonable_encoder(tag_id)}", + method="DELETE", + json={ + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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) diff --git a/src/intercom/tags/types/__init__.py b/src/intercom/tags/types/__init__.py new file mode 100644 index 00000000..e05e3643 --- /dev/null +++ b/src/intercom/tags/types/__init__.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .tag import Tag + from .tag_basic import TagBasic + from .tags_create_request_body import TagsCreateRequestBody +_dynamic_imports: typing.Dict[str, str] = { + "Tag": ".tag", + "TagBasic": ".tag_basic", + "TagsCreateRequestBody": ".tags_create_request_body", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Tag", "TagBasic", "TagsCreateRequestBody"] diff --git a/src/intercom/tags/types/tag.py b/src/intercom/tags/types/tag.py new file mode 100644 index 00000000..fe9be061 --- /dev/null +++ b/src/intercom/tags/types/tag.py @@ -0,0 +1,45 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.reference import Reference + + +class Tag(UncheckedBaseModel): + """ + A tag allows you to label your contacts, companies, and conversations and list them using that tag. + """ + + type: typing.Literal["tag"] = pydantic.Field(default="tag") + """ + value is "tag" + """ + + id: str = pydantic.Field() + """ + The id of the tag + """ + + name: str = pydantic.Field() + """ + The name of the tag + """ + + applied_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the tag was applied to the object + """ + + applied_by: typing.Optional[Reference] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/tags/types/tag_basic.py b/src/intercom/tags/types/tag_basic.py new file mode 100644 index 00000000..0eaac93b --- /dev/null +++ b/src/intercom/tags/types/tag_basic.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class TagBasic(UncheckedBaseModel): + """ + A tag allows you to label your contacts, companies, and conversations and list them using that tag. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + value is "tag" + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the tag + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the tag + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/tags/types/tags_create_request_body.py b/src/intercom/tags/types/tags_create_request_body.py new file mode 100644 index 00000000..ca21183c --- /dev/null +++ b/src/intercom/tags/types/tags_create_request_body.py @@ -0,0 +1,12 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...types.create_or_update_tag_request import CreateOrUpdateTagRequest +from ...types.tag_company_request import TagCompanyRequest +from ...types.tag_multiple_users_request import TagMultipleUsersRequest +from ...types.untag_company_request import UntagCompanyRequest + +TagsCreateRequestBody = typing.Union[ + CreateOrUpdateTagRequest, TagCompanyRequest, UntagCompanyRequest, TagMultipleUsersRequest +] diff --git a/src/intercom/teams/__init__.py b/src/intercom/teams/__init__.py new file mode 100644 index 00000000..0d71c0eb --- /dev/null +++ b/src/intercom/teams/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import Team +_dynamic_imports: typing.Dict[str, str] = {"Team": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Team"] diff --git a/src/intercom/teams/client.py b/src/intercom/teams/client.py new file mode 100644 index 00000000..a3657cdb --- /dev/null +++ b/src/intercom/teams/client.py @@ -0,0 +1,171 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.team_list import TeamList +from .raw_client import AsyncRawTeamsClient, RawTeamsClient +from .types.team import Team + + +class TeamsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawTeamsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawTeamsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawTeamsClient + """ + return self._raw_client + + def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> TeamList: + """ + This will return a list of team objects for the App. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TeamList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.teams.list() + """ + _response = self._raw_client.list(request_options=request_options) + return _response.data + + def find(self, team_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Team: + """ + You can fetch the details of a single team, containing an array of admins that belong to this team. + + Parameters + ---------- + team_id : str + The unique identifier of a given team. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Team + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.teams.find( + team_id="123", + ) + """ + _response = self._raw_client.find(team_id, request_options=request_options) + return _response.data + + +class AsyncTeamsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawTeamsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawTeamsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawTeamsClient + """ + return self._raw_client + + async def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> TeamList: + """ + This will return a list of team objects for the App. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TeamList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.teams.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list(request_options=request_options) + return _response.data + + async def find(self, team_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Team: + """ + You can fetch the details of a single team, containing an array of admins that belong to this team. + + Parameters + ---------- + team_id : str + The unique identifier of a given team. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Team + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.teams.find( + team_id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.find(team_id, request_options=request_options) + return _response.data diff --git a/src/intercom/teams/raw_client.py b/src/intercom/teams/raw_client.py new file mode 100644 index 00000000..a8fa31b9 --- /dev/null +++ b/src/intercom/teams/raw_client.py @@ -0,0 +1,236 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ..core.unchecked_base_model import construct_type +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from ..types.team_list import TeamList +from .types.team import Team + + +class RawTeamsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[TeamList]: + """ + This will return a list of team objects for the App. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[TeamList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "teams", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TeamList, + construct_type( + type_=TeamList, # 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 find(self, team_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Team]: + """ + You can fetch the details of a single team, containing an array of admins that belong to this team. + + Parameters + ---------- + team_id : str + The unique identifier of a given team. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Team] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"teams/{jsonable_encoder(team_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Team, + construct_type( + type_=Team, # 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) + + +class AsyncRawTeamsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> AsyncHttpResponse[TeamList]: + """ + This will return a list of team objects for the App. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[TeamList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "teams", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TeamList, + construct_type( + type_=TeamList, # 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 find( + self, team_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Team]: + """ + You can fetch the details of a single team, containing an array of admins that belong to this team. + + Parameters + ---------- + team_id : str + The unique identifier of a given team. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Team] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"teams/{jsonable_encoder(team_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Team, + construct_type( + type_=Team, # 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) diff --git a/src/intercom/teams/types/__init__.py b/src/intercom/teams/types/__init__.py new file mode 100644 index 00000000..b3e03912 --- /dev/null +++ b/src/intercom/teams/types/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .team import Team +_dynamic_imports: typing.Dict[str, str] = {"Team": ".team"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Team"] diff --git a/src/intercom/teams/types/team.py b/src/intercom/teams/types/team.py new file mode 100644 index 00000000..2c1cb83b --- /dev/null +++ b/src/intercom/teams/types/team.py @@ -0,0 +1,54 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.admin_priority_level import AdminPriorityLevel + + +class Team(UncheckedBaseModel): + """ + Teams are groups of admins in Intercom. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + Value is always "team" + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the team + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the team + """ + + admin_ids: typing.Optional[typing.List[int]] = pydantic.Field(default=None) + """ + The list of admin IDs that are a part of the team. + """ + + admin_priority_level: typing.Optional[AdminPriorityLevel] = None + assignment_limit: typing.Optional[int] = pydantic.Field(default=None) + """ + The assignment limit for the team. This field is only present when the team's distribution type is load balanced. + """ + + distribution_method: typing.Optional[str] = pydantic.Field(default=None) + """ + Describes how assignments are distributed among the team members + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/ticket_states/__init__.py b/src/intercom/ticket_states/__init__.py new file mode 100644 index 00000000..5cde0202 --- /dev/null +++ b/src/intercom/ticket_states/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/src/intercom/ticket_states/client.py b/src/intercom/ticket_states/client.py new file mode 100644 index 00000000..f61c4c59 --- /dev/null +++ b/src/intercom/ticket_states/client.py @@ -0,0 +1,100 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.ticket_state_list import TicketStateList +from .raw_client import AsyncRawTicketStatesClient, RawTicketStatesClient + + +class TicketStatesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawTicketStatesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawTicketStatesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawTicketStatesClient + """ + return self._raw_client + + def list_ticket_states(self, *, request_options: typing.Optional[RequestOptions] = None) -> TicketStateList: + """ + You can get a list of all ticket states for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TicketStateList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.ticket_states.list_ticket_states() + """ + _response = self._raw_client.list_ticket_states(request_options=request_options) + return _response.data + + +class AsyncTicketStatesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawTicketStatesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawTicketStatesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawTicketStatesClient + """ + return self._raw_client + + async def list_ticket_states(self, *, request_options: typing.Optional[RequestOptions] = None) -> TicketStateList: + """ + You can get a list of all ticket states for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TicketStateList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.ticket_states.list_ticket_states() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_ticket_states(request_options=request_options) + return _response.data diff --git a/src/intercom/ticket_states/raw_client.py b/src/intercom/ticket_states/raw_client.py new file mode 100644 index 00000000..b42d5777 --- /dev/null +++ b/src/intercom/ticket_states/raw_client.py @@ -0,0 +1,117 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.http_response import AsyncHttpResponse, HttpResponse +from ..core.request_options import RequestOptions +from ..core.unchecked_base_model import construct_type +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from ..types.ticket_state_list import TicketStateList + + +class RawTicketStatesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_ticket_states( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[TicketStateList]: + """ + You can get a list of all ticket states for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[TicketStateList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "ticket_states", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TicketStateList, + construct_type( + type_=TicketStateList, # 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) + + +class AsyncRawTicketStatesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_ticket_states( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[TicketStateList]: + """ + You can get a list of all ticket states for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[TicketStateList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "ticket_states", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TicketStateList, + construct_type( + type_=TicketStateList, # 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) diff --git a/src/intercom/ticket_types/__init__.py b/src/intercom/ticket_types/__init__.py new file mode 100644 index 00000000..d2acd5fc --- /dev/null +++ b/src/intercom/ticket_types/__init__.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import UpdateTicketTypeRequestCategory + from . import attributes + from .attributes import CreateTicketTypeAttributeRequestDataType +_dynamic_imports: typing.Dict[str, str] = { + "CreateTicketTypeAttributeRequestDataType": ".attributes", + "UpdateTicketTypeRequestCategory": ".types", + "attributes": ".attributes", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["CreateTicketTypeAttributeRequestDataType", "UpdateTicketTypeRequestCategory", "attributes"] diff --git a/src/intercom/ticket_types/attributes/__init__.py b/src/intercom/ticket_types/attributes/__init__.py new file mode 100644 index 00000000..12db5aa8 --- /dev/null +++ b/src/intercom/ticket_types/attributes/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import CreateTicketTypeAttributeRequestDataType +_dynamic_imports: typing.Dict[str, str] = {"CreateTicketTypeAttributeRequestDataType": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["CreateTicketTypeAttributeRequestDataType"] diff --git a/src/intercom/ticket_types/attributes/client.py b/src/intercom/ticket_types/attributes/client.py new file mode 100644 index 00000000..443b17a9 --- /dev/null +++ b/src/intercom/ticket_types/attributes/client.py @@ -0,0 +1,438 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ...types.ticket_type_attribute import TicketTypeAttribute +from .raw_client import AsyncRawAttributesClient, RawAttributesClient +from .types.create_ticket_type_attribute_request_data_type import CreateTicketTypeAttributeRequestDataType + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class AttributesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawAttributesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawAttributesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawAttributesClient + """ + return self._raw_client + + def create( + self, + ticket_type_id: str, + *, + name: str, + description: str, + data_type: CreateTicketTypeAttributeRequestDataType, + required_to_create: typing.Optional[bool] = OMIT, + required_to_create_for_contacts: typing.Optional[bool] = OMIT, + visible_on_create: typing.Optional[bool] = OMIT, + visible_to_contacts: typing.Optional[bool] = OMIT, + multiline: typing.Optional[bool] = OMIT, + list_items: typing.Optional[str] = OMIT, + allow_multiple_values: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[TicketTypeAttribute]: + """ + You can create a new attribute for a ticket type. + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + name : str + The name of the ticket type attribute + + description : str + The description of the attribute presented to the teammate or contact + + data_type : CreateTicketTypeAttributeRequestDataType + The data type of the attribute + + required_to_create : typing.Optional[bool] + Whether the attribute is required to be filled in when teammates are creating the ticket in Inbox. + + required_to_create_for_contacts : typing.Optional[bool] + Whether the attribute is required to be filled in when contacts are creating the ticket in Messenger. + + visible_on_create : typing.Optional[bool] + Whether the attribute is visible to teammates when creating a ticket in Inbox. + + visible_to_contacts : typing.Optional[bool] + Whether the attribute is visible to contacts when creating a ticket in Messenger. + + multiline : typing.Optional[bool] + Whether the attribute allows multiple lines of text (only applicable to string attributes) + + list_items : typing.Optional[str] + A comma delimited list of items for the attribute value (only applicable to list attributes) + + allow_multiple_values : typing.Optional[bool] + Whether the attribute allows multiple files to be attached to it (only applicable to file attributes) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[TicketTypeAttribute] + Ticket Type Attribute created + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.ticket_types.attributes.create( + ticket_type_id="ticket_type_id", + name="Attribute Title", + description="Attribute Description", + data_type="string", + required_to_create=False, + ) + """ + _response = self._raw_client.create( + ticket_type_id, + name=name, + description=description, + data_type=data_type, + required_to_create=required_to_create, + required_to_create_for_contacts=required_to_create_for_contacts, + visible_on_create=visible_on_create, + visible_to_contacts=visible_to_contacts, + multiline=multiline, + list_items=list_items, + allow_multiple_values=allow_multiple_values, + request_options=request_options, + ) + return _response.data + + def update( + self, + ticket_type_id: str, + attribute_id: str, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + required_to_create: typing.Optional[bool] = OMIT, + required_to_create_for_contacts: typing.Optional[bool] = OMIT, + visible_on_create: typing.Optional[bool] = OMIT, + visible_to_contacts: typing.Optional[bool] = OMIT, + multiline: typing.Optional[bool] = OMIT, + list_items: typing.Optional[str] = OMIT, + allow_multiple_values: typing.Optional[bool] = OMIT, + archived: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[TicketTypeAttribute]: + """ + You can update an existing attribute for a ticket type. + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + attribute_id : str + The unique identifier for the ticket type attribute which is given by Intercom. + + name : typing.Optional[str] + The name of the ticket type attribute + + description : typing.Optional[str] + The description of the attribute presented to the teammate or contact + + required_to_create : typing.Optional[bool] + Whether the attribute is required to be filled in when teammates are creating the ticket in Inbox. + + required_to_create_for_contacts : typing.Optional[bool] + Whether the attribute is required to be filled in when contacts are creating the ticket in Messenger. + + visible_on_create : typing.Optional[bool] + Whether the attribute is visible to teammates when creating a ticket in Inbox. + + visible_to_contacts : typing.Optional[bool] + Whether the attribute is visible to contacts when creating a ticket in Messenger. + + multiline : typing.Optional[bool] + Whether the attribute allows multiple lines of text (only applicable to string attributes) + + list_items : typing.Optional[str] + A comma delimited list of items for the attribute value (only applicable to list attributes) + + allow_multiple_values : typing.Optional[bool] + Whether the attribute allows multiple files to be attached to it (only applicable to file attributes) + + archived : typing.Optional[bool] + Whether the attribute should be archived and not shown during creation of the ticket (it will still be present on previously created tickets) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[TicketTypeAttribute] + Ticket Type Attribute updated + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.ticket_types.attributes.update( + ticket_type_id="ticket_type_id", + attribute_id="attribute_id", + description="New Attribute Description", + ) + """ + _response = self._raw_client.update( + ticket_type_id, + attribute_id, + name=name, + description=description, + required_to_create=required_to_create, + required_to_create_for_contacts=required_to_create_for_contacts, + visible_on_create=visible_on_create, + visible_to_contacts=visible_to_contacts, + multiline=multiline, + list_items=list_items, + allow_multiple_values=allow_multiple_values, + archived=archived, + request_options=request_options, + ) + return _response.data + + +class AsyncAttributesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawAttributesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawAttributesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawAttributesClient + """ + return self._raw_client + + async def create( + self, + ticket_type_id: str, + *, + name: str, + description: str, + data_type: CreateTicketTypeAttributeRequestDataType, + required_to_create: typing.Optional[bool] = OMIT, + required_to_create_for_contacts: typing.Optional[bool] = OMIT, + visible_on_create: typing.Optional[bool] = OMIT, + visible_to_contacts: typing.Optional[bool] = OMIT, + multiline: typing.Optional[bool] = OMIT, + list_items: typing.Optional[str] = OMIT, + allow_multiple_values: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[TicketTypeAttribute]: + """ + You can create a new attribute for a ticket type. + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + name : str + The name of the ticket type attribute + + description : str + The description of the attribute presented to the teammate or contact + + data_type : CreateTicketTypeAttributeRequestDataType + The data type of the attribute + + required_to_create : typing.Optional[bool] + Whether the attribute is required to be filled in when teammates are creating the ticket in Inbox. + + required_to_create_for_contacts : typing.Optional[bool] + Whether the attribute is required to be filled in when contacts are creating the ticket in Messenger. + + visible_on_create : typing.Optional[bool] + Whether the attribute is visible to teammates when creating a ticket in Inbox. + + visible_to_contacts : typing.Optional[bool] + Whether the attribute is visible to contacts when creating a ticket in Messenger. + + multiline : typing.Optional[bool] + Whether the attribute allows multiple lines of text (only applicable to string attributes) + + list_items : typing.Optional[str] + A comma delimited list of items for the attribute value (only applicable to list attributes) + + allow_multiple_values : typing.Optional[bool] + Whether the attribute allows multiple files to be attached to it (only applicable to file attributes) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[TicketTypeAttribute] + Ticket Type Attribute created + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.ticket_types.attributes.create( + ticket_type_id="ticket_type_id", + name="Attribute Title", + description="Attribute Description", + data_type="string", + required_to_create=False, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + ticket_type_id, + name=name, + description=description, + data_type=data_type, + required_to_create=required_to_create, + required_to_create_for_contacts=required_to_create_for_contacts, + visible_on_create=visible_on_create, + visible_to_contacts=visible_to_contacts, + multiline=multiline, + list_items=list_items, + allow_multiple_values=allow_multiple_values, + request_options=request_options, + ) + return _response.data + + async def update( + self, + ticket_type_id: str, + attribute_id: str, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + required_to_create: typing.Optional[bool] = OMIT, + required_to_create_for_contacts: typing.Optional[bool] = OMIT, + visible_on_create: typing.Optional[bool] = OMIT, + visible_to_contacts: typing.Optional[bool] = OMIT, + multiline: typing.Optional[bool] = OMIT, + list_items: typing.Optional[str] = OMIT, + allow_multiple_values: typing.Optional[bool] = OMIT, + archived: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[TicketTypeAttribute]: + """ + You can update an existing attribute for a ticket type. + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + attribute_id : str + The unique identifier for the ticket type attribute which is given by Intercom. + + name : typing.Optional[str] + The name of the ticket type attribute + + description : typing.Optional[str] + The description of the attribute presented to the teammate or contact + + required_to_create : typing.Optional[bool] + Whether the attribute is required to be filled in when teammates are creating the ticket in Inbox. + + required_to_create_for_contacts : typing.Optional[bool] + Whether the attribute is required to be filled in when contacts are creating the ticket in Messenger. + + visible_on_create : typing.Optional[bool] + Whether the attribute is visible to teammates when creating a ticket in Inbox. + + visible_to_contacts : typing.Optional[bool] + Whether the attribute is visible to contacts when creating a ticket in Messenger. + + multiline : typing.Optional[bool] + Whether the attribute allows multiple lines of text (only applicable to string attributes) + + list_items : typing.Optional[str] + A comma delimited list of items for the attribute value (only applicable to list attributes) + + allow_multiple_values : typing.Optional[bool] + Whether the attribute allows multiple files to be attached to it (only applicable to file attributes) + + archived : typing.Optional[bool] + Whether the attribute should be archived and not shown during creation of the ticket (it will still be present on previously created tickets) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[TicketTypeAttribute] + Ticket Type Attribute updated + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.ticket_types.attributes.update( + ticket_type_id="ticket_type_id", + attribute_id="attribute_id", + description="New Attribute Description", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update( + ticket_type_id, + attribute_id, + name=name, + description=description, + required_to_create=required_to_create, + required_to_create_for_contacts=required_to_create_for_contacts, + visible_on_create=visible_on_create, + visible_to_contacts=visible_to_contacts, + multiline=multiline, + list_items=list_items, + allow_multiple_values=allow_multiple_values, + archived=archived, + request_options=request_options, + ) + return _response.data diff --git a/src/intercom/ticket_types/attributes/raw_client.py b/src/intercom/ticket_types/attributes/raw_client.py new file mode 100644 index 00000000..4e47e008 --- /dev/null +++ b/src/intercom/ticket_types/attributes/raw_client.py @@ -0,0 +1,480 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ...errors.unauthorized_error import UnauthorizedError +from ...types.error import Error +from ...types.ticket_type_attribute import TicketTypeAttribute +from .types.create_ticket_type_attribute_request_data_type import CreateTicketTypeAttributeRequestDataType + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawAttributesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def create( + self, + ticket_type_id: str, + *, + name: str, + description: str, + data_type: CreateTicketTypeAttributeRequestDataType, + required_to_create: typing.Optional[bool] = OMIT, + required_to_create_for_contacts: typing.Optional[bool] = OMIT, + visible_on_create: typing.Optional[bool] = OMIT, + visible_to_contacts: typing.Optional[bool] = OMIT, + multiline: typing.Optional[bool] = OMIT, + list_items: typing.Optional[str] = OMIT, + allow_multiple_values: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[typing.Optional[TicketTypeAttribute]]: + """ + You can create a new attribute for a ticket type. + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + name : str + The name of the ticket type attribute + + description : str + The description of the attribute presented to the teammate or contact + + data_type : CreateTicketTypeAttributeRequestDataType + The data type of the attribute + + required_to_create : typing.Optional[bool] + Whether the attribute is required to be filled in when teammates are creating the ticket in Inbox. + + required_to_create_for_contacts : typing.Optional[bool] + Whether the attribute is required to be filled in when contacts are creating the ticket in Messenger. + + visible_on_create : typing.Optional[bool] + Whether the attribute is visible to teammates when creating a ticket in Inbox. + + visible_to_contacts : typing.Optional[bool] + Whether the attribute is visible to contacts when creating a ticket in Messenger. + + multiline : typing.Optional[bool] + Whether the attribute allows multiple lines of text (only applicable to string attributes) + + list_items : typing.Optional[str] + A comma delimited list of items for the attribute value (only applicable to list attributes) + + allow_multiple_values : typing.Optional[bool] + Whether the attribute allows multiple files to be attached to it (only applicable to file attributes) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[TicketTypeAttribute]] + Ticket Type Attribute created + """ + _response = self._client_wrapper.httpx_client.request( + f"ticket_types/{jsonable_encoder(ticket_type_id)}/attributes", + method="POST", + json={ + "name": name, + "description": description, + "data_type": data_type, + "required_to_create": required_to_create, + "required_to_create_for_contacts": required_to_create_for_contacts, + "visible_on_create": visible_on_create, + "visible_to_contacts": visible_to_contacts, + "multiline": multiline, + "list_items": list_items, + "allow_multiple_values": allow_multiple_values, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[TicketTypeAttribute], + construct_type( + type_=typing.Optional[TicketTypeAttribute], # 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, + ticket_type_id: str, + attribute_id: str, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + required_to_create: typing.Optional[bool] = OMIT, + required_to_create_for_contacts: typing.Optional[bool] = OMIT, + visible_on_create: typing.Optional[bool] = OMIT, + visible_to_contacts: typing.Optional[bool] = OMIT, + multiline: typing.Optional[bool] = OMIT, + list_items: typing.Optional[str] = OMIT, + allow_multiple_values: typing.Optional[bool] = OMIT, + archived: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[typing.Optional[TicketTypeAttribute]]: + """ + You can update an existing attribute for a ticket type. + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + attribute_id : str + The unique identifier for the ticket type attribute which is given by Intercom. + + name : typing.Optional[str] + The name of the ticket type attribute + + description : typing.Optional[str] + The description of the attribute presented to the teammate or contact + + required_to_create : typing.Optional[bool] + Whether the attribute is required to be filled in when teammates are creating the ticket in Inbox. + + required_to_create_for_contacts : typing.Optional[bool] + Whether the attribute is required to be filled in when contacts are creating the ticket in Messenger. + + visible_on_create : typing.Optional[bool] + Whether the attribute is visible to teammates when creating a ticket in Inbox. + + visible_to_contacts : typing.Optional[bool] + Whether the attribute is visible to contacts when creating a ticket in Messenger. + + multiline : typing.Optional[bool] + Whether the attribute allows multiple lines of text (only applicable to string attributes) + + list_items : typing.Optional[str] + A comma delimited list of items for the attribute value (only applicable to list attributes) + + allow_multiple_values : typing.Optional[bool] + Whether the attribute allows multiple files to be attached to it (only applicable to file attributes) + + archived : typing.Optional[bool] + Whether the attribute should be archived and not shown during creation of the ticket (it will still be present on previously created tickets) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[TicketTypeAttribute]] + Ticket Type Attribute updated + """ + _response = self._client_wrapper.httpx_client.request( + f"ticket_types/{jsonable_encoder(ticket_type_id)}/attributes/{jsonable_encoder(attribute_id)}", + method="PUT", + json={ + "name": name, + "description": description, + "required_to_create": required_to_create, + "required_to_create_for_contacts": required_to_create_for_contacts, + "visible_on_create": visible_on_create, + "visible_to_contacts": visible_to_contacts, + "multiline": multiline, + "list_items": list_items, + "allow_multiple_values": allow_multiple_values, + "archived": archived, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[TicketTypeAttribute], + construct_type( + type_=typing.Optional[TicketTypeAttribute], # 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) + + +class AsyncRawAttributesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def create( + self, + ticket_type_id: str, + *, + name: str, + description: str, + data_type: CreateTicketTypeAttributeRequestDataType, + required_to_create: typing.Optional[bool] = OMIT, + required_to_create_for_contacts: typing.Optional[bool] = OMIT, + visible_on_create: typing.Optional[bool] = OMIT, + visible_to_contacts: typing.Optional[bool] = OMIT, + multiline: typing.Optional[bool] = OMIT, + list_items: typing.Optional[str] = OMIT, + allow_multiple_values: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[typing.Optional[TicketTypeAttribute]]: + """ + You can create a new attribute for a ticket type. + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + name : str + The name of the ticket type attribute + + description : str + The description of the attribute presented to the teammate or contact + + data_type : CreateTicketTypeAttributeRequestDataType + The data type of the attribute + + required_to_create : typing.Optional[bool] + Whether the attribute is required to be filled in when teammates are creating the ticket in Inbox. + + required_to_create_for_contacts : typing.Optional[bool] + Whether the attribute is required to be filled in when contacts are creating the ticket in Messenger. + + visible_on_create : typing.Optional[bool] + Whether the attribute is visible to teammates when creating a ticket in Inbox. + + visible_to_contacts : typing.Optional[bool] + Whether the attribute is visible to contacts when creating a ticket in Messenger. + + multiline : typing.Optional[bool] + Whether the attribute allows multiple lines of text (only applicable to string attributes) + + list_items : typing.Optional[str] + A comma delimited list of items for the attribute value (only applicable to list attributes) + + allow_multiple_values : typing.Optional[bool] + Whether the attribute allows multiple files to be attached to it (only applicable to file attributes) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[TicketTypeAttribute]] + Ticket Type Attribute created + """ + _response = await self._client_wrapper.httpx_client.request( + f"ticket_types/{jsonable_encoder(ticket_type_id)}/attributes", + method="POST", + json={ + "name": name, + "description": description, + "data_type": data_type, + "required_to_create": required_to_create, + "required_to_create_for_contacts": required_to_create_for_contacts, + "visible_on_create": visible_on_create, + "visible_to_contacts": visible_to_contacts, + "multiline": multiline, + "list_items": list_items, + "allow_multiple_values": allow_multiple_values, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[TicketTypeAttribute], + construct_type( + type_=typing.Optional[TicketTypeAttribute], # 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, + ticket_type_id: str, + attribute_id: str, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + required_to_create: typing.Optional[bool] = OMIT, + required_to_create_for_contacts: typing.Optional[bool] = OMIT, + visible_on_create: typing.Optional[bool] = OMIT, + visible_to_contacts: typing.Optional[bool] = OMIT, + multiline: typing.Optional[bool] = OMIT, + list_items: typing.Optional[str] = OMIT, + allow_multiple_values: typing.Optional[bool] = OMIT, + archived: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[typing.Optional[TicketTypeAttribute]]: + """ + You can update an existing attribute for a ticket type. + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + attribute_id : str + The unique identifier for the ticket type attribute which is given by Intercom. + + name : typing.Optional[str] + The name of the ticket type attribute + + description : typing.Optional[str] + The description of the attribute presented to the teammate or contact + + required_to_create : typing.Optional[bool] + Whether the attribute is required to be filled in when teammates are creating the ticket in Inbox. + + required_to_create_for_contacts : typing.Optional[bool] + Whether the attribute is required to be filled in when contacts are creating the ticket in Messenger. + + visible_on_create : typing.Optional[bool] + Whether the attribute is visible to teammates when creating a ticket in Inbox. + + visible_to_contacts : typing.Optional[bool] + Whether the attribute is visible to contacts when creating a ticket in Messenger. + + multiline : typing.Optional[bool] + Whether the attribute allows multiple lines of text (only applicable to string attributes) + + list_items : typing.Optional[str] + A comma delimited list of items for the attribute value (only applicable to list attributes) + + allow_multiple_values : typing.Optional[bool] + Whether the attribute allows multiple files to be attached to it (only applicable to file attributes) + + archived : typing.Optional[bool] + Whether the attribute should be archived and not shown during creation of the ticket (it will still be present on previously created tickets) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[TicketTypeAttribute]] + Ticket Type Attribute updated + """ + _response = await self._client_wrapper.httpx_client.request( + f"ticket_types/{jsonable_encoder(ticket_type_id)}/attributes/{jsonable_encoder(attribute_id)}", + method="PUT", + json={ + "name": name, + "description": description, + "required_to_create": required_to_create, + "required_to_create_for_contacts": required_to_create_for_contacts, + "visible_on_create": visible_on_create, + "visible_to_contacts": visible_to_contacts, + "multiline": multiline, + "list_items": list_items, + "allow_multiple_values": allow_multiple_values, + "archived": archived, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[TicketTypeAttribute], + construct_type( + type_=typing.Optional[TicketTypeAttribute], # 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) diff --git a/src/intercom/ticket_types/attributes/types/__init__.py b/src/intercom/ticket_types/attributes/types/__init__.py new file mode 100644 index 00000000..2670a4cf --- /dev/null +++ b/src/intercom/ticket_types/attributes/types/__init__.py @@ -0,0 +1,36 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .create_ticket_type_attribute_request_data_type import CreateTicketTypeAttributeRequestDataType +_dynamic_imports: typing.Dict[str, str] = { + "CreateTicketTypeAttributeRequestDataType": ".create_ticket_type_attribute_request_data_type" +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["CreateTicketTypeAttributeRequestDataType"] diff --git a/src/intercom/ticket_types/attributes/types/create_ticket_type_attribute_request_data_type.py b/src/intercom/ticket_types/attributes/types/create_ticket_type_attribute_request_data_type.py new file mode 100644 index 00000000..af58adf3 --- /dev/null +++ b/src/intercom/ticket_types/attributes/types/create_ticket_type_attribute_request_data_type.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CreateTicketTypeAttributeRequestDataType = typing.Union[ + typing.Literal["string", "list", "integer", "decimal", "boolean", "datetime", "files"], typing.Any +] diff --git a/src/intercom/ticket_types/client.py b/src/intercom/ticket_types/client.py new file mode 100644 index 00000000..b2eb02ac --- /dev/null +++ b/src/intercom/ticket_types/client.py @@ -0,0 +1,458 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from ..tickets.types.ticket_type import TicketType +from ..types.create_ticket_type_request import CreateTicketTypeRequest +from ..types.ticket_type_list import TicketTypeList +from .raw_client import AsyncRawTicketTypesClient, RawTicketTypesClient +from .types.update_ticket_type_request_category import UpdateTicketTypeRequestCategory + +if typing.TYPE_CHECKING: + from .attributes.client import AsyncAttributesClient, AttributesClient +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class TicketTypesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawTicketTypesClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._attributes: typing.Optional[AttributesClient] = None + + @property + def with_raw_response(self) -> RawTicketTypesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawTicketTypesClient + """ + return self._raw_client + + def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> TicketTypeList: + """ + You can get a list of all ticket types for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TicketTypeList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.ticket_types.list() + """ + _response = self._raw_client.list(request_options=request_options) + return _response.data + + def create( + self, + *, + request: typing.Optional[CreateTicketTypeRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[TicketType]: + """ + You can create a new ticket type. + > 📘 Creating ticket types. + > + > Every ticket type will be created with two default attributes: _default_title_ and _default_description_. + > For the `icon` propery, use an emoji from [Twemoji Cheatsheet](https://twemoji-cheatsheet.vercel.app/) + + Parameters + ---------- + request : typing.Optional[CreateTicketTypeRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[TicketType] + Ticket type created + + Examples + -------- + from intercom import CreateTicketTypeRequest, Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.ticket_types.create( + request=CreateTicketTypeRequest( + name="Customer Issue", + description="Customer Report Template", + category="Customer", + icon="🎟️", + ), + ) + """ + _response = self._raw_client.create(request=request, request_options=request_options) + return _response.data + + def get( + self, ticket_type_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[TicketType]: + """ + You can fetch the details of a single ticket type. + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[TicketType] + Ticket type found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.ticket_types.get( + ticket_type_id="ticket_type_id", + ) + """ + _response = self._raw_client.get(ticket_type_id, request_options=request_options) + return _response.data + + def update( + self, + ticket_type_id: str, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + category: typing.Optional[UpdateTicketTypeRequestCategory] = OMIT, + icon: typing.Optional[str] = OMIT, + archived: typing.Optional[bool] = OMIT, + is_internal: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[TicketType]: + """ + + You can update a ticket type. + + > 📘 Updating a ticket type. + > + > For the `icon` propery, use an emoji from [Twemoji Cheatsheet](https://twemoji-cheatsheet.vercel.app/) + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + name : typing.Optional[str] + The name of the ticket type. + + description : typing.Optional[str] + The description of the ticket type. + + category : typing.Optional[UpdateTicketTypeRequestCategory] + Category of the Ticket Type. + + icon : typing.Optional[str] + The icon of the ticket type. + + archived : typing.Optional[bool] + The archived status of the ticket type. + + is_internal : typing.Optional[bool] + Whether the tickets associated with this ticket type are intended for internal use only or will be shared with customers. This is currently a limited attribute. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[TicketType] + Ticket type updated + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.ticket_types.update( + ticket_type_id="ticket_type_id", + name="Bug Report 2", + ) + """ + _response = self._raw_client.update( + ticket_type_id, + name=name, + description=description, + category=category, + icon=icon, + archived=archived, + is_internal=is_internal, + request_options=request_options, + ) + return _response.data + + @property + def attributes(self): + if self._attributes is None: + from .attributes.client import AttributesClient # noqa: E402 + + self._attributes = AttributesClient(client_wrapper=self._client_wrapper) + return self._attributes + + +class AsyncTicketTypesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawTicketTypesClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._attributes: typing.Optional[AsyncAttributesClient] = None + + @property + def with_raw_response(self) -> AsyncRawTicketTypesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawTicketTypesClient + """ + return self._raw_client + + async def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> TicketTypeList: + """ + You can get a list of all ticket types for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TicketTypeList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.ticket_types.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list(request_options=request_options) + return _response.data + + async def create( + self, + *, + request: typing.Optional[CreateTicketTypeRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[TicketType]: + """ + You can create a new ticket type. + > 📘 Creating ticket types. + > + > Every ticket type will be created with two default attributes: _default_title_ and _default_description_. + > For the `icon` propery, use an emoji from [Twemoji Cheatsheet](https://twemoji-cheatsheet.vercel.app/) + + Parameters + ---------- + request : typing.Optional[CreateTicketTypeRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[TicketType] + Ticket type created + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom, CreateTicketTypeRequest + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.ticket_types.create( + request=CreateTicketTypeRequest( + name="Customer Issue", + description="Customer Report Template", + category="Customer", + icon="🎟️", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create(request=request, request_options=request_options) + return _response.data + + async def get( + self, ticket_type_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[TicketType]: + """ + You can fetch the details of a single ticket type. + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[TicketType] + Ticket type found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.ticket_types.get( + ticket_type_id="ticket_type_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get(ticket_type_id, request_options=request_options) + return _response.data + + async def update( + self, + ticket_type_id: str, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + category: typing.Optional[UpdateTicketTypeRequestCategory] = OMIT, + icon: typing.Optional[str] = OMIT, + archived: typing.Optional[bool] = OMIT, + is_internal: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[TicketType]: + """ + + You can update a ticket type. + + > 📘 Updating a ticket type. + > + > For the `icon` propery, use an emoji from [Twemoji Cheatsheet](https://twemoji-cheatsheet.vercel.app/) + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + name : typing.Optional[str] + The name of the ticket type. + + description : typing.Optional[str] + The description of the ticket type. + + category : typing.Optional[UpdateTicketTypeRequestCategory] + Category of the Ticket Type. + + icon : typing.Optional[str] + The icon of the ticket type. + + archived : typing.Optional[bool] + The archived status of the ticket type. + + is_internal : typing.Optional[bool] + Whether the tickets associated with this ticket type are intended for internal use only or will be shared with customers. This is currently a limited attribute. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[TicketType] + Ticket type updated + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.ticket_types.update( + ticket_type_id="ticket_type_id", + name="Bug Report 2", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update( + ticket_type_id, + name=name, + description=description, + category=category, + icon=icon, + archived=archived, + is_internal=is_internal, + request_options=request_options, + ) + return _response.data + + @property + def attributes(self): + if self._attributes is None: + from .attributes.client import AsyncAttributesClient # noqa: E402 + + self._attributes = AsyncAttributesClient(client_wrapper=self._client_wrapper) + return self._attributes diff --git a/src/intercom/ticket_types/raw_client.py b/src/intercom/ticket_types/raw_client.py new file mode 100644 index 00000000..7e70ed67 --- /dev/null +++ b/src/intercom/ticket_types/raw_client.py @@ -0,0 +1,549 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ..core.serialization import convert_and_respect_annotation_metadata +from ..core.unchecked_base_model import construct_type +from ..errors.unauthorized_error import UnauthorizedError +from ..tickets.types.ticket_type import TicketType +from ..types.create_ticket_type_request import CreateTicketTypeRequest +from ..types.error import Error +from ..types.ticket_type_list import TicketTypeList +from .types.update_ticket_type_request_category import UpdateTicketTypeRequestCategory + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawTicketTypesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[TicketTypeList]: + """ + You can get a list of all ticket types for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[TicketTypeList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "ticket_types", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TicketTypeList, + construct_type( + type_=TicketTypeList, # 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 create( + self, + *, + request: typing.Optional[CreateTicketTypeRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[typing.Optional[TicketType]]: + """ + You can create a new ticket type. + > 📘 Creating ticket types. + > + > Every ticket type will be created with two default attributes: _default_title_ and _default_description_. + > For the `icon` propery, use an emoji from [Twemoji Cheatsheet](https://twemoji-cheatsheet.vercel.app/) + + Parameters + ---------- + request : typing.Optional[CreateTicketTypeRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[TicketType]] + Ticket type created + """ + _response = self._client_wrapper.httpx_client.request( + "ticket_types", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateTicketTypeRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[TicketType], + construct_type( + type_=typing.Optional[TicketType], # 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 get( + self, ticket_type_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[typing.Optional[TicketType]]: + """ + You can fetch the details of a single ticket type. + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[TicketType]] + Ticket type found + """ + _response = self._client_wrapper.httpx_client.request( + f"ticket_types/{jsonable_encoder(ticket_type_id)}", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[TicketType], + construct_type( + type_=typing.Optional[TicketType], # 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, + ticket_type_id: str, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + category: typing.Optional[UpdateTicketTypeRequestCategory] = OMIT, + icon: typing.Optional[str] = OMIT, + archived: typing.Optional[bool] = OMIT, + is_internal: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[typing.Optional[TicketType]]: + """ + + You can update a ticket type. + + > 📘 Updating a ticket type. + > + > For the `icon` propery, use an emoji from [Twemoji Cheatsheet](https://twemoji-cheatsheet.vercel.app/) + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + name : typing.Optional[str] + The name of the ticket type. + + description : typing.Optional[str] + The description of the ticket type. + + category : typing.Optional[UpdateTicketTypeRequestCategory] + Category of the Ticket Type. + + icon : typing.Optional[str] + The icon of the ticket type. + + archived : typing.Optional[bool] + The archived status of the ticket type. + + is_internal : typing.Optional[bool] + Whether the tickets associated with this ticket type are intended for internal use only or will be shared with customers. This is currently a limited attribute. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[TicketType]] + Ticket type updated + """ + _response = self._client_wrapper.httpx_client.request( + f"ticket_types/{jsonable_encoder(ticket_type_id)}", + method="PUT", + json={ + "name": name, + "description": description, + "category": category, + "icon": icon, + "archived": archived, + "is_internal": is_internal, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[TicketType], + construct_type( + type_=typing.Optional[TicketType], # 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) + + +class AsyncRawTicketTypesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[TicketTypeList]: + """ + You can get a list of all ticket types for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[TicketTypeList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "ticket_types", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TicketTypeList, + construct_type( + type_=TicketTypeList, # 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 create( + self, + *, + request: typing.Optional[CreateTicketTypeRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[typing.Optional[TicketType]]: + """ + You can create a new ticket type. + > 📘 Creating ticket types. + > + > Every ticket type will be created with two default attributes: _default_title_ and _default_description_. + > For the `icon` propery, use an emoji from [Twemoji Cheatsheet](https://twemoji-cheatsheet.vercel.app/) + + Parameters + ---------- + request : typing.Optional[CreateTicketTypeRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[TicketType]] + Ticket type created + """ + _response = await self._client_wrapper.httpx_client.request( + "ticket_types", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateTicketTypeRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[TicketType], + construct_type( + type_=typing.Optional[TicketType], # 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 get( + self, ticket_type_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[typing.Optional[TicketType]]: + """ + You can fetch the details of a single ticket type. + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[TicketType]] + Ticket type found + """ + _response = await self._client_wrapper.httpx_client.request( + f"ticket_types/{jsonable_encoder(ticket_type_id)}", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[TicketType], + construct_type( + type_=typing.Optional[TicketType], # 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, + ticket_type_id: str, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + category: typing.Optional[UpdateTicketTypeRequestCategory] = OMIT, + icon: typing.Optional[str] = OMIT, + archived: typing.Optional[bool] = OMIT, + is_internal: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[typing.Optional[TicketType]]: + """ + + You can update a ticket type. + + > 📘 Updating a ticket type. + > + > For the `icon` propery, use an emoji from [Twemoji Cheatsheet](https://twemoji-cheatsheet.vercel.app/) + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + name : typing.Optional[str] + The name of the ticket type. + + description : typing.Optional[str] + The description of the ticket type. + + category : typing.Optional[UpdateTicketTypeRequestCategory] + Category of the Ticket Type. + + icon : typing.Optional[str] + The icon of the ticket type. + + archived : typing.Optional[bool] + The archived status of the ticket type. + + is_internal : typing.Optional[bool] + Whether the tickets associated with this ticket type are intended for internal use only or will be shared with customers. This is currently a limited attribute. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[TicketType]] + Ticket type updated + """ + _response = await self._client_wrapper.httpx_client.request( + f"ticket_types/{jsonable_encoder(ticket_type_id)}", + method="PUT", + json={ + "name": name, + "description": description, + "category": category, + "icon": icon, + "archived": archived, + "is_internal": is_internal, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[TicketType], + construct_type( + type_=typing.Optional[TicketType], # 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) diff --git a/src/intercom/ticket_types/types/__init__.py b/src/intercom/ticket_types/types/__init__.py new file mode 100644 index 00000000..7739caf7 --- /dev/null +++ b/src/intercom/ticket_types/types/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .update_ticket_type_request_category import UpdateTicketTypeRequestCategory +_dynamic_imports: typing.Dict[str, str] = {"UpdateTicketTypeRequestCategory": ".update_ticket_type_request_category"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["UpdateTicketTypeRequestCategory"] diff --git a/src/intercom/ticket_types/types/update_ticket_type_request_category.py b/src/intercom/ticket_types/types/update_ticket_type_request_category.py new file mode 100644 index 00000000..9dd149ee --- /dev/null +++ b/src/intercom/ticket_types/types/update_ticket_type_request_category.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +UpdateTicketTypeRequestCategory = typing.Union[typing.Literal["Customer", "Back-office", "Tracker"], typing.Any] diff --git a/src/intercom/tickets/__init__.py b/src/intercom/tickets/__init__.py new file mode 100644 index 00000000..9054f74d --- /dev/null +++ b/src/intercom/tickets/__init__.py @@ -0,0 +1,100 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + DeleteTicketResponse, + Ticket, + TicketCategory, + TicketContacts, + TicketPart, + TicketPartPreviousTicketState, + TicketPartTicketState, + TicketPartUpdatedAttributeData, + TicketPartUpdatedAttributeDataAttribute, + TicketPartUpdatedAttributeDataValue, + TicketPartUpdatedAttributeDataValueId, + TicketPartUpdatedAttributeDataValueLabel, + TicketState, + TicketStateCategory, + TicketStateDetailed, + TicketStateDetailedCategory, + TicketStateDetailedTicketTypes, + TicketType, + TicketTypeCategory, + TicketTypeTicketStates, + TicketsReplyRequestBody, + ) +_dynamic_imports: typing.Dict[str, str] = { + "DeleteTicketResponse": ".types", + "Ticket": ".types", + "TicketCategory": ".types", + "TicketContacts": ".types", + "TicketPart": ".types", + "TicketPartPreviousTicketState": ".types", + "TicketPartTicketState": ".types", + "TicketPartUpdatedAttributeData": ".types", + "TicketPartUpdatedAttributeDataAttribute": ".types", + "TicketPartUpdatedAttributeDataValue": ".types", + "TicketPartUpdatedAttributeDataValueId": ".types", + "TicketPartUpdatedAttributeDataValueLabel": ".types", + "TicketState": ".types", + "TicketStateCategory": ".types", + "TicketStateDetailed": ".types", + "TicketStateDetailedCategory": ".types", + "TicketStateDetailedTicketTypes": ".types", + "TicketType": ".types", + "TicketTypeCategory": ".types", + "TicketTypeTicketStates": ".types", + "TicketsReplyRequestBody": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "DeleteTicketResponse", + "Ticket", + "TicketCategory", + "TicketContacts", + "TicketPart", + "TicketPartPreviousTicketState", + "TicketPartTicketState", + "TicketPartUpdatedAttributeData", + "TicketPartUpdatedAttributeDataAttribute", + "TicketPartUpdatedAttributeDataValue", + "TicketPartUpdatedAttributeDataValueId", + "TicketPartUpdatedAttributeDataValueLabel", + "TicketState", + "TicketStateCategory", + "TicketStateDetailed", + "TicketStateDetailedCategory", + "TicketStateDetailedTicketTypes", + "TicketType", + "TicketTypeCategory", + "TicketTypeTicketStates", + "TicketsReplyRequestBody", +] diff --git a/src/intercom/tickets/client.py b/src/intercom/tickets/client.py new file mode 100644 index 00000000..2c4974e1 --- /dev/null +++ b/src/intercom/tickets/client.py @@ -0,0 +1,1064 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.pagination import AsyncPager, SyncPager +from ..core.request_options import RequestOptions +from ..jobs.types.jobs import Jobs +from ..types.create_ticket_request_assignment import CreateTicketRequestAssignment +from ..types.create_ticket_request_contacts_item import CreateTicketRequestContactsItem +from ..types.search_request_query import SearchRequestQuery +from ..types.starting_after_paging import StartingAfterPaging +from ..types.ticket_list import TicketList +from ..types.ticket_reply import TicketReply +from .raw_client import AsyncRawTicketsClient, RawTicketsClient +from .types.delete_ticket_response import DeleteTicketResponse +from .types.ticket import Ticket +from .types.tickets_reply_request_body import TicketsReplyRequestBody + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class TicketsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawTicketsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawTicketsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawTicketsClient + """ + return self._raw_client + + def reply( + self, + ticket_id: str, + *, + request: TicketsReplyRequestBody, + request_options: typing.Optional[RequestOptions] = None, + ) -> TicketReply: + """ + You can reply to a ticket with a message from an admin or on behalf of a contact, or with a note for admins. + + Parameters + ---------- + ticket_id : str + + request : TicketsReplyRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TicketReply + Admin Reply to send Quick Reply Options + + Examples + -------- + from intercom import ContactReplyTicketIntercomUserIdRequest, Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.tickets.reply( + ticket_id="123", + request=ContactReplyTicketIntercomUserIdRequest( + body="Thanks again :)", + intercom_user_id="6762f2971bb69f9f2193bc49", + ), + ) + """ + _response = self._raw_client.reply(ticket_id, request=request, request_options=request_options) + return _response.data + + def create( + self, + *, + ticket_type_id: str, + contacts: typing.Sequence[CreateTicketRequestContactsItem], + skip_notifications: typing.Optional[bool] = OMIT, + conversation_to_link_id: typing.Optional[str] = OMIT, + company_id: typing.Optional[str] = OMIT, + created_at: typing.Optional[int] = OMIT, + assignment: typing.Optional[CreateTicketRequestAssignment] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[Ticket]: + """ + You can create a new ticket. + + Parameters + ---------- + ticket_type_id : str + The ID of the type of ticket you want to create + + contacts : typing.Sequence[CreateTicketRequestContactsItem] + The list of contacts (users or leads) affected by this ticket. Currently only one is allowed + + skip_notifications : typing.Optional[bool] + Option to disable notifications when a Ticket is created. + + conversation_to_link_id : typing.Optional[str] + The ID of the conversation you want to link to the ticket. Here are the valid ways of linking two tickets: + - conversation | back-office ticket + - customer tickets | non-shared back-office ticket + - conversation | tracker ticket + - customer ticket | tracker ticket + + company_id : typing.Optional[str] + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom + + created_at : typing.Optional[int] + The time the ticket was created. If not provided, the current time will be used. + + assignment : typing.Optional[CreateTicketRequestAssignment] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Ticket] + Successful response + + Examples + -------- + from intercom import CreateTicketRequestContactsItemId, Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.tickets.create( + ticket_type_id="1234", + contacts=[ + CreateTicketRequestContactsItemId( + id="6762f2d81bb69f9f2193bc54", + ) + ], + ) + """ + _response = self._raw_client.create( + ticket_type_id=ticket_type_id, + contacts=contacts, + skip_notifications=skip_notifications, + conversation_to_link_id=conversation_to_link_id, + company_id=company_id, + created_at=created_at, + assignment=assignment, + request_options=request_options, + ) + return _response.data + + def enqueue_create_ticket( + self, + *, + ticket_type_id: str, + contacts: typing.Sequence[CreateTicketRequestContactsItem], + skip_notifications: typing.Optional[bool] = OMIT, + conversation_to_link_id: typing.Optional[str] = OMIT, + company_id: typing.Optional[str] = OMIT, + created_at: typing.Optional[int] = OMIT, + assignment: typing.Optional[CreateTicketRequestAssignment] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Jobs: + """ + Enqueues ticket creation for asynchronous processing, returning if the job was enqueued successfully to be processed. We attempt to perform a best-effort validation on inputs before tasks are enqueued. If the given parameters are incorrect, we won't enqueue the job. + + Parameters + ---------- + ticket_type_id : str + The ID of the type of ticket you want to create + + contacts : typing.Sequence[CreateTicketRequestContactsItem] + The list of contacts (users or leads) affected by this ticket. Currently only one is allowed + + skip_notifications : typing.Optional[bool] + Option to disable notifications when a Ticket is created. + + conversation_to_link_id : typing.Optional[str] + The ID of the conversation you want to link to the ticket. Here are the valid ways of linking two tickets: + - conversation | back-office ticket + - customer tickets | non-shared back-office ticket + - conversation | tracker ticket + - customer ticket | tracker ticket + + company_id : typing.Optional[str] + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom + + created_at : typing.Optional[int] + The time the ticket was created. If not provided, the current time will be used. + + assignment : typing.Optional[CreateTicketRequestAssignment] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Jobs + Successful response + + Examples + -------- + from intercom import CreateTicketRequestContactsItemId, Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.tickets.enqueue_create_ticket( + ticket_type_id="1234", + contacts=[ + CreateTicketRequestContactsItemId( + id="6762f2d81bb69f9f2193bc54", + ) + ], + ) + """ + _response = self._raw_client.enqueue_create_ticket( + ticket_type_id=ticket_type_id, + contacts=contacts, + skip_notifications=skip_notifications, + conversation_to_link_id=conversation_to_link_id, + company_id=company_id, + created_at=created_at, + assignment=assignment, + request_options=request_options, + ) + return _response.data + + def get( + self, ticket_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[Ticket]: + """ + You can fetch the details of a single ticket. + + Parameters + ---------- + ticket_id : str + The unique identifier for the ticket which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Ticket] + Ticket found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.tickets.get( + ticket_id="ticket_id", + ) + """ + _response = self._raw_client.get(ticket_id, request_options=request_options) + return _response.data + + def update( + self, + ticket_id: str, + *, + ticket_attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + ticket_state_id: typing.Optional[str] = OMIT, + company_id: typing.Optional[str] = OMIT, + open: typing.Optional[bool] = OMIT, + is_shared: typing.Optional[bool] = OMIT, + snoozed_until: typing.Optional[int] = OMIT, + admin_id: typing.Optional[int] = OMIT, + assignee_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[Ticket]: + """ + You can update a ticket. + + Parameters + ---------- + ticket_id : str + The unique identifier for the ticket which is given by Intercom + + ticket_attributes : typing.Optional[typing.Dict[str, typing.Any]] + The attributes set on the ticket. + + ticket_state_id : typing.Optional[str] + The ID of the ticket state associated with the ticket type. + + company_id : typing.Optional[str] + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom. Set to nil to remove company. + + open : typing.Optional[bool] + Specify if a ticket is open. Set to false to close a ticket. Closing a ticket will also unsnooze it. + + is_shared : typing.Optional[bool] + Specify whether the ticket is visible to users. + + snoozed_until : typing.Optional[int] + The time you want the ticket to reopen. + + admin_id : typing.Optional[int] + The ID of the admin performing ticket update. Needed for workflows execution and attributing actions to specific admins. + + assignee_id : typing.Optional[str] + The ID of the admin or team to which the ticket is assigned. Set this 0 to unassign it. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Ticket] + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.tickets.update( + ticket_id="ticket_id", + ticket_attributes={ + "_default_title_": "example", + "_default_description_": "there is a problem", + }, + ticket_state_id="123", + open=True, + snoozed_until=1673609604, + admin_id=991268011, + assignee_id="123", + ) + """ + _response = self._raw_client.update( + ticket_id, + ticket_attributes=ticket_attributes, + ticket_state_id=ticket_state_id, + company_id=company_id, + open=open, + is_shared=is_shared, + snoozed_until=snoozed_until, + admin_id=admin_id, + assignee_id=assignee_id, + request_options=request_options, + ) + return _response.data + + def delete_ticket( + self, ticket_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeleteTicketResponse: + """ + You can delete a ticket using the Intercom provided ID. + + Parameters + ---------- + ticket_id : str + The unique identifier for the ticket which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteTicketResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.tickets.delete_ticket( + ticket_id="ticket_id", + ) + """ + _response = self._raw_client.delete_ticket(ticket_id, request_options=request_options) + return _response.data + + def search( + self, + *, + query: SearchRequestQuery, + pagination: typing.Optional[StartingAfterPaging] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[typing.Optional[Ticket], TicketList]: + """ + You can search for multiple tickets by the value of their attributes in order to fetch exactly which ones you want. + + To search for tickets, you send a `POST` request to `https://api.intercom.io/tickets/search`. + + This will accept a query object in the body which will define your filters. + {% 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 `20` 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 %} + + ### 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 multiples 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 + + ### Accepted Fields + + Most keys listed as part of the Ticket 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 `"foobar"`). + The `source.body` field is unique as the search will not be performed against the entire value, but instead against every element of the value separately. For example, when searching for a conversation with a `"I need support"` body - the query should contain a `=` operator with the value `"support"` for such conversation to be returned. A query with a `=` operator and a `"need support"` value will not yield a result. + + | Field | Type | + | :---------------------------------------- | :--------------------------------------------------------------------------------------- | + | id | String | + | created_at | Date (UNIX timestamp) | + | updated_at | Date (UNIX timestamp) | + | title | String | + | description | String | + | category | String | + | ticket_type_id | String | + | contact_ids | String | + | teammate_ids | String | + | admin_assignee_id | String | + | team_assignee_id | String | + | open | Boolean | + | state | String | + | snoozed_until | Date (UNIX timestamp) | + | ticket_attribute.{id} | String or Boolean or Date (UNIX timestamp) or Float or Integer | + + {% admonition type="info" name="Searching by Category" %} + When searching for tickets by the **`category`** field, specific terms must be used instead of the category names: + * For **Customer** category tickets, use the term `request`. + * For **Back-office** category tickets, use the term `task`. + * For **Tracker** category tickets, use the term `tracker`. + {% /admonition %} + + ### Accepted Operators + + {% admonition type="info" name="Searching based on `created_at`" %} + You may 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 Shortcut for `OR` queries Values most be in Array | + | NIN | All | Not In Shortcut for `OR !` queries Values must be in Array | + | > | Integer Date (UNIX Timestamp) | Greater (or equal) than | + | < | Integer Date (UNIX Timestamp) | Lower (or equal) 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[typing.Optional[Ticket], TicketList] + successful + + Examples + -------- + from intercom import ( + Intercom, + MultipleFilterSearchRequest, + SingleFilterSearchRequest, + StartingAfterPaging, + ) + + client = Intercom( + token="YOUR_TOKEN", + ) + response = client.tickets.search( + query=MultipleFilterSearchRequest( + operator="AND", + value=[ + SingleFilterSearchRequest( + field="created_at", + operator=">", + value="1306054154", + ) + ], + ), + pagination=StartingAfterPaging( + per_page=5, + ), + ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.search(query=query, pagination=pagination, request_options=request_options) + + +class AsyncTicketsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawTicketsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawTicketsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawTicketsClient + """ + return self._raw_client + + async def reply( + self, + ticket_id: str, + *, + request: TicketsReplyRequestBody, + request_options: typing.Optional[RequestOptions] = None, + ) -> TicketReply: + """ + You can reply to a ticket with a message from an admin or on behalf of a contact, or with a note for admins. + + Parameters + ---------- + ticket_id : str + + request : TicketsReplyRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TicketReply + Admin Reply to send Quick Reply Options + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom, ContactReplyTicketIntercomUserIdRequest + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.tickets.reply( + ticket_id="123", + request=ContactReplyTicketIntercomUserIdRequest( + body="Thanks again :)", + intercom_user_id="6762f2971bb69f9f2193bc49", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.reply(ticket_id, request=request, request_options=request_options) + return _response.data + + async def create( + self, + *, + ticket_type_id: str, + contacts: typing.Sequence[CreateTicketRequestContactsItem], + skip_notifications: typing.Optional[bool] = OMIT, + conversation_to_link_id: typing.Optional[str] = OMIT, + company_id: typing.Optional[str] = OMIT, + created_at: typing.Optional[int] = OMIT, + assignment: typing.Optional[CreateTicketRequestAssignment] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[Ticket]: + """ + You can create a new ticket. + + Parameters + ---------- + ticket_type_id : str + The ID of the type of ticket you want to create + + contacts : typing.Sequence[CreateTicketRequestContactsItem] + The list of contacts (users or leads) affected by this ticket. Currently only one is allowed + + skip_notifications : typing.Optional[bool] + Option to disable notifications when a Ticket is created. + + conversation_to_link_id : typing.Optional[str] + The ID of the conversation you want to link to the ticket. Here are the valid ways of linking two tickets: + - conversation | back-office ticket + - customer tickets | non-shared back-office ticket + - conversation | tracker ticket + - customer ticket | tracker ticket + + company_id : typing.Optional[str] + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom + + created_at : typing.Optional[int] + The time the ticket was created. If not provided, the current time will be used. + + assignment : typing.Optional[CreateTicketRequestAssignment] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Ticket] + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom, CreateTicketRequestContactsItemId + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.tickets.create( + ticket_type_id="1234", + contacts=[ + CreateTicketRequestContactsItemId( + id="6762f2d81bb69f9f2193bc54", + ) + ], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + ticket_type_id=ticket_type_id, + contacts=contacts, + skip_notifications=skip_notifications, + conversation_to_link_id=conversation_to_link_id, + company_id=company_id, + created_at=created_at, + assignment=assignment, + request_options=request_options, + ) + return _response.data + + async def enqueue_create_ticket( + self, + *, + ticket_type_id: str, + contacts: typing.Sequence[CreateTicketRequestContactsItem], + skip_notifications: typing.Optional[bool] = OMIT, + conversation_to_link_id: typing.Optional[str] = OMIT, + company_id: typing.Optional[str] = OMIT, + created_at: typing.Optional[int] = OMIT, + assignment: typing.Optional[CreateTicketRequestAssignment] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Jobs: + """ + Enqueues ticket creation for asynchronous processing, returning if the job was enqueued successfully to be processed. We attempt to perform a best-effort validation on inputs before tasks are enqueued. If the given parameters are incorrect, we won't enqueue the job. + + Parameters + ---------- + ticket_type_id : str + The ID of the type of ticket you want to create + + contacts : typing.Sequence[CreateTicketRequestContactsItem] + The list of contacts (users or leads) affected by this ticket. Currently only one is allowed + + skip_notifications : typing.Optional[bool] + Option to disable notifications when a Ticket is created. + + conversation_to_link_id : typing.Optional[str] + The ID of the conversation you want to link to the ticket. Here are the valid ways of linking two tickets: + - conversation | back-office ticket + - customer tickets | non-shared back-office ticket + - conversation | tracker ticket + - customer ticket | tracker ticket + + company_id : typing.Optional[str] + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom + + created_at : typing.Optional[int] + The time the ticket was created. If not provided, the current time will be used. + + assignment : typing.Optional[CreateTicketRequestAssignment] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Jobs + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom, CreateTicketRequestContactsItemId + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.tickets.enqueue_create_ticket( + ticket_type_id="1234", + contacts=[ + CreateTicketRequestContactsItemId( + id="6762f2d81bb69f9f2193bc54", + ) + ], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.enqueue_create_ticket( + ticket_type_id=ticket_type_id, + contacts=contacts, + skip_notifications=skip_notifications, + conversation_to_link_id=conversation_to_link_id, + company_id=company_id, + created_at=created_at, + assignment=assignment, + request_options=request_options, + ) + return _response.data + + async def get( + self, ticket_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[Ticket]: + """ + You can fetch the details of a single ticket. + + Parameters + ---------- + ticket_id : str + The unique identifier for the ticket which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Ticket] + Ticket found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.tickets.get( + ticket_id="ticket_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get(ticket_id, request_options=request_options) + return _response.data + + async def update( + self, + ticket_id: str, + *, + ticket_attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + ticket_state_id: typing.Optional[str] = OMIT, + company_id: typing.Optional[str] = OMIT, + open: typing.Optional[bool] = OMIT, + is_shared: typing.Optional[bool] = OMIT, + snoozed_until: typing.Optional[int] = OMIT, + admin_id: typing.Optional[int] = OMIT, + assignee_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[Ticket]: + """ + You can update a ticket. + + Parameters + ---------- + ticket_id : str + The unique identifier for the ticket which is given by Intercom + + ticket_attributes : typing.Optional[typing.Dict[str, typing.Any]] + The attributes set on the ticket. + + ticket_state_id : typing.Optional[str] + The ID of the ticket state associated with the ticket type. + + company_id : typing.Optional[str] + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom. Set to nil to remove company. + + open : typing.Optional[bool] + Specify if a ticket is open. Set to false to close a ticket. Closing a ticket will also unsnooze it. + + is_shared : typing.Optional[bool] + Specify whether the ticket is visible to users. + + snoozed_until : typing.Optional[int] + The time you want the ticket to reopen. + + admin_id : typing.Optional[int] + The ID of the admin performing ticket update. Needed for workflows execution and attributing actions to specific admins. + + assignee_id : typing.Optional[str] + The ID of the admin or team to which the ticket is assigned. Set this 0 to unassign it. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Ticket] + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.tickets.update( + ticket_id="ticket_id", + ticket_attributes={ + "_default_title_": "example", + "_default_description_": "there is a problem", + }, + ticket_state_id="123", + open=True, + snoozed_until=1673609604, + admin_id=991268011, + assignee_id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update( + ticket_id, + ticket_attributes=ticket_attributes, + ticket_state_id=ticket_state_id, + company_id=company_id, + open=open, + is_shared=is_shared, + snoozed_until=snoozed_until, + admin_id=admin_id, + assignee_id=assignee_id, + request_options=request_options, + ) + return _response.data + + async def delete_ticket( + self, ticket_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeleteTicketResponse: + """ + You can delete a ticket using the Intercom provided ID. + + Parameters + ---------- + ticket_id : str + The unique identifier for the ticket which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteTicketResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.tickets.delete_ticket( + ticket_id="ticket_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_ticket(ticket_id, request_options=request_options) + return _response.data + + async def search( + self, + *, + query: SearchRequestQuery, + pagination: typing.Optional[StartingAfterPaging] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[typing.Optional[Ticket], TicketList]: + """ + You can search for multiple tickets by the value of their attributes in order to fetch exactly which ones you want. + + To search for tickets, you send a `POST` request to `https://api.intercom.io/tickets/search`. + + This will accept a query object in the body which will define your filters. + {% 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 `20` 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 %} + + ### 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 multiples 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 + + ### Accepted Fields + + Most keys listed as part of the Ticket 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 `"foobar"`). + The `source.body` field is unique as the search will not be performed against the entire value, but instead against every element of the value separately. For example, when searching for a conversation with a `"I need support"` body - the query should contain a `=` operator with the value `"support"` for such conversation to be returned. A query with a `=` operator and a `"need support"` value will not yield a result. + + | Field | Type | + | :---------------------------------------- | :--------------------------------------------------------------------------------------- | + | id | String | + | created_at | Date (UNIX timestamp) | + | updated_at | Date (UNIX timestamp) | + | title | String | + | description | String | + | category | String | + | ticket_type_id | String | + | contact_ids | String | + | teammate_ids | String | + | admin_assignee_id | String | + | team_assignee_id | String | + | open | Boolean | + | state | String | + | snoozed_until | Date (UNIX timestamp) | + | ticket_attribute.{id} | String or Boolean or Date (UNIX timestamp) or Float or Integer | + + {% admonition type="info" name="Searching by Category" %} + When searching for tickets by the **`category`** field, specific terms must be used instead of the category names: + * For **Customer** category tickets, use the term `request`. + * For **Back-office** category tickets, use the term `task`. + * For **Tracker** category tickets, use the term `tracker`. + {% /admonition %} + + ### Accepted Operators + + {% admonition type="info" name="Searching based on `created_at`" %} + You may 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 Shortcut for `OR` queries Values most be in Array | + | NIN | All | Not In Shortcut for `OR !` queries Values must be in Array | + | > | Integer Date (UNIX Timestamp) | Greater (or equal) than | + | < | Integer Date (UNIX Timestamp) | Lower (or equal) 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[typing.Optional[Ticket], TicketList] + successful + + Examples + -------- + import asyncio + + from intercom import ( + AsyncIntercom, + MultipleFilterSearchRequest, + SingleFilterSearchRequest, + StartingAfterPaging, + ) + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + response = await client.tickets.search( + query=MultipleFilterSearchRequest( + operator="AND", + value=[ + SingleFilterSearchRequest( + field="created_at", + operator=">", + value="1306054154", + ) + ], + ), + pagination=StartingAfterPaging( + per_page=5, + ), + ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page + + + asyncio.run(main()) + """ + return await self._raw_client.search(query=query, pagination=pagination, request_options=request_options) diff --git a/src/intercom/tickets/raw_client.py b/src/intercom/tickets/raw_client.py new file mode 100644 index 00000000..dd24252d --- /dev/null +++ b/src/intercom/tickets/raw_client.py @@ -0,0 +1,1367 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.bad_request_error import BadRequestError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..jobs.types.jobs import Jobs +from ..types.create_ticket_request_assignment import CreateTicketRequestAssignment +from ..types.create_ticket_request_contacts_item import CreateTicketRequestContactsItem +from ..types.error import Error +from ..types.search_request_query import SearchRequestQuery +from ..types.starting_after_paging import StartingAfterPaging +from ..types.ticket_list import TicketList +from ..types.ticket_reply import TicketReply +from .types.delete_ticket_response import DeleteTicketResponse +from .types.ticket import Ticket +from .types.tickets_reply_request_body import TicketsReplyRequestBody + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawTicketsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def reply( + self, + ticket_id: str, + *, + request: TicketsReplyRequestBody, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[TicketReply]: + """ + You can reply to a ticket with a message from an admin or on behalf of a contact, or with a note for admins. + + Parameters + ---------- + ticket_id : str + + request : TicketsReplyRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[TicketReply] + Admin Reply to send Quick Reply Options + """ + _response = self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(ticket_id)}/reply", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=TicketsReplyRequestBody, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TicketReply, + construct_type( + type_=TicketReply, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 create( + self, + *, + ticket_type_id: str, + contacts: typing.Sequence[CreateTicketRequestContactsItem], + skip_notifications: typing.Optional[bool] = OMIT, + conversation_to_link_id: typing.Optional[str] = OMIT, + company_id: typing.Optional[str] = OMIT, + created_at: typing.Optional[int] = OMIT, + assignment: typing.Optional[CreateTicketRequestAssignment] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[typing.Optional[Ticket]]: + """ + You can create a new ticket. + + Parameters + ---------- + ticket_type_id : str + The ID of the type of ticket you want to create + + contacts : typing.Sequence[CreateTicketRequestContactsItem] + The list of contacts (users or leads) affected by this ticket. Currently only one is allowed + + skip_notifications : typing.Optional[bool] + Option to disable notifications when a Ticket is created. + + conversation_to_link_id : typing.Optional[str] + The ID of the conversation you want to link to the ticket. Here are the valid ways of linking two tickets: + - conversation | back-office ticket + - customer tickets | non-shared back-office ticket + - conversation | tracker ticket + - customer ticket | tracker ticket + + company_id : typing.Optional[str] + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom + + created_at : typing.Optional[int] + The time the ticket was created. If not provided, the current time will be used. + + assignment : typing.Optional[CreateTicketRequestAssignment] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[Ticket]] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "tickets", + method="POST", + json={ + "skip_notifications": skip_notifications, + "ticket_type_id": ticket_type_id, + "contacts": convert_and_respect_annotation_metadata( + object_=contacts, annotation=typing.Sequence[CreateTicketRequestContactsItem], direction="write" + ), + "conversation_to_link_id": conversation_to_link_id, + "company_id": company_id, + "created_at": created_at, + "assignment": convert_and_respect_annotation_metadata( + object_=assignment, annotation=CreateTicketRequestAssignment, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Ticket], + construct_type( + type_=typing.Optional[Ticket], # 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 enqueue_create_ticket( + self, + *, + ticket_type_id: str, + contacts: typing.Sequence[CreateTicketRequestContactsItem], + skip_notifications: typing.Optional[bool] = OMIT, + conversation_to_link_id: typing.Optional[str] = OMIT, + company_id: typing.Optional[str] = OMIT, + created_at: typing.Optional[int] = OMIT, + assignment: typing.Optional[CreateTicketRequestAssignment] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Jobs]: + """ + Enqueues ticket creation for asynchronous processing, returning if the job was enqueued successfully to be processed. We attempt to perform a best-effort validation on inputs before tasks are enqueued. If the given parameters are incorrect, we won't enqueue the job. + + Parameters + ---------- + ticket_type_id : str + The ID of the type of ticket you want to create + + contacts : typing.Sequence[CreateTicketRequestContactsItem] + The list of contacts (users or leads) affected by this ticket. Currently only one is allowed + + skip_notifications : typing.Optional[bool] + Option to disable notifications when a Ticket is created. + + conversation_to_link_id : typing.Optional[str] + The ID of the conversation you want to link to the ticket. Here are the valid ways of linking two tickets: + - conversation | back-office ticket + - customer tickets | non-shared back-office ticket + - conversation | tracker ticket + - customer ticket | tracker ticket + + company_id : typing.Optional[str] + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom + + created_at : typing.Optional[int] + The time the ticket was created. If not provided, the current time will be used. + + assignment : typing.Optional[CreateTicketRequestAssignment] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Jobs] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "tickets/enqueue", + method="POST", + json={ + "skip_notifications": skip_notifications, + "ticket_type_id": ticket_type_id, + "contacts": convert_and_respect_annotation_metadata( + object_=contacts, annotation=typing.Sequence[CreateTicketRequestContactsItem], direction="write" + ), + "conversation_to_link_id": conversation_to_link_id, + "company_id": company_id, + "created_at": created_at, + "assignment": convert_and_respect_annotation_metadata( + object_=assignment, annotation=CreateTicketRequestAssignment, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Jobs, + construct_type( + type_=Jobs, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 get( + self, ticket_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[typing.Optional[Ticket]]: + """ + You can fetch the details of a single ticket. + + Parameters + ---------- + ticket_id : str + The unique identifier for the ticket which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[Ticket]] + Ticket found + """ + _response = self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(ticket_id)}", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Ticket], + construct_type( + type_=typing.Optional[Ticket], # 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, + ticket_id: str, + *, + ticket_attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + ticket_state_id: typing.Optional[str] = OMIT, + company_id: typing.Optional[str] = OMIT, + open: typing.Optional[bool] = OMIT, + is_shared: typing.Optional[bool] = OMIT, + snoozed_until: typing.Optional[int] = OMIT, + admin_id: typing.Optional[int] = OMIT, + assignee_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[typing.Optional[Ticket]]: + """ + You can update a ticket. + + Parameters + ---------- + ticket_id : str + The unique identifier for the ticket which is given by Intercom + + ticket_attributes : typing.Optional[typing.Dict[str, typing.Any]] + The attributes set on the ticket. + + ticket_state_id : typing.Optional[str] + The ID of the ticket state associated with the ticket type. + + company_id : typing.Optional[str] + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom. Set to nil to remove company. + + open : typing.Optional[bool] + Specify if a ticket is open. Set to false to close a ticket. Closing a ticket will also unsnooze it. + + is_shared : typing.Optional[bool] + Specify whether the ticket is visible to users. + + snoozed_until : typing.Optional[int] + The time you want the ticket to reopen. + + admin_id : typing.Optional[int] + The ID of the admin performing ticket update. Needed for workflows execution and attributing actions to specific admins. + + assignee_id : typing.Optional[str] + The ID of the admin or team to which the ticket is assigned. Set this 0 to unassign it. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[Ticket]] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(ticket_id)}", + method="PUT", + json={ + "ticket_attributes": ticket_attributes, + "ticket_state_id": ticket_state_id, + "company_id": company_id, + "open": open, + "is_shared": is_shared, + "snoozed_until": snoozed_until, + "admin_id": admin_id, + "assignee_id": assignee_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Ticket], + construct_type( + type_=typing.Optional[Ticket], # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 delete_ticket( + self, ticket_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DeleteTicketResponse]: + """ + You can delete a ticket using the Intercom provided ID. + + Parameters + ---------- + ticket_id : str + The unique identifier for the ticket which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeleteTicketResponse] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(ticket_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteTicketResponse, + construct_type( + type_=DeleteTicketResponse, # 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 search( + self, + *, + query: SearchRequestQuery, + pagination: typing.Optional[StartingAfterPaging] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[typing.Optional[Ticket], TicketList]: + """ + You can search for multiple tickets by the value of their attributes in order to fetch exactly which ones you want. + + To search for tickets, you send a `POST` request to `https://api.intercom.io/tickets/search`. + + This will accept a query object in the body which will define your filters. + {% 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 `20` 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 %} + + ### 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 multiples 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 + + ### Accepted Fields + + Most keys listed as part of the Ticket 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 `"foobar"`). + The `source.body` field is unique as the search will not be performed against the entire value, but instead against every element of the value separately. For example, when searching for a conversation with a `"I need support"` body - the query should contain a `=` operator with the value `"support"` for such conversation to be returned. A query with a `=` operator and a `"need support"` value will not yield a result. + + | Field | Type | + | :---------------------------------------- | :--------------------------------------------------------------------------------------- | + | id | String | + | created_at | Date (UNIX timestamp) | + | updated_at | Date (UNIX timestamp) | + | title | String | + | description | String | + | category | String | + | ticket_type_id | String | + | contact_ids | String | + | teammate_ids | String | + | admin_assignee_id | String | + | team_assignee_id | String | + | open | Boolean | + | state | String | + | snoozed_until | Date (UNIX timestamp) | + | ticket_attribute.{id} | String or Boolean or Date (UNIX timestamp) or Float or Integer | + + {% admonition type="info" name="Searching by Category" %} + When searching for tickets by the **`category`** field, specific terms must be used instead of the category names: + * For **Customer** category tickets, use the term `request`. + * For **Back-office** category tickets, use the term `task`. + * For **Tracker** category tickets, use the term `tracker`. + {% /admonition %} + + ### Accepted Operators + + {% admonition type="info" name="Searching based on `created_at`" %} + You may 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 Shortcut for `OR` queries Values most be in Array | + | NIN | All | Not In Shortcut for `OR !` queries Values must be in Array | + | > | Integer Date (UNIX Timestamp) | Greater (or equal) than | + | < | Integer Date (UNIX Timestamp) | Lower (or equal) 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[typing.Optional[Ticket], TicketList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "tickets/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( + TicketList, + construct_type( + type_=TicketList, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.tickets + _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) + _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 AsyncRawTicketsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def reply( + self, + ticket_id: str, + *, + request: TicketsReplyRequestBody, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[TicketReply]: + """ + You can reply to a ticket with a message from an admin or on behalf of a contact, or with a note for admins. + + Parameters + ---------- + ticket_id : str + + request : TicketsReplyRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[TicketReply] + Admin Reply to send Quick Reply Options + """ + _response = await self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(ticket_id)}/reply", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=TicketsReplyRequestBody, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TicketReply, + construct_type( + type_=TicketReply, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 create( + self, + *, + ticket_type_id: str, + contacts: typing.Sequence[CreateTicketRequestContactsItem], + skip_notifications: typing.Optional[bool] = OMIT, + conversation_to_link_id: typing.Optional[str] = OMIT, + company_id: typing.Optional[str] = OMIT, + created_at: typing.Optional[int] = OMIT, + assignment: typing.Optional[CreateTicketRequestAssignment] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[typing.Optional[Ticket]]: + """ + You can create a new ticket. + + Parameters + ---------- + ticket_type_id : str + The ID of the type of ticket you want to create + + contacts : typing.Sequence[CreateTicketRequestContactsItem] + The list of contacts (users or leads) affected by this ticket. Currently only one is allowed + + skip_notifications : typing.Optional[bool] + Option to disable notifications when a Ticket is created. + + conversation_to_link_id : typing.Optional[str] + The ID of the conversation you want to link to the ticket. Here are the valid ways of linking two tickets: + - conversation | back-office ticket + - customer tickets | non-shared back-office ticket + - conversation | tracker ticket + - customer ticket | tracker ticket + + company_id : typing.Optional[str] + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom + + created_at : typing.Optional[int] + The time the ticket was created. If not provided, the current time will be used. + + assignment : typing.Optional[CreateTicketRequestAssignment] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[Ticket]] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "tickets", + method="POST", + json={ + "skip_notifications": skip_notifications, + "ticket_type_id": ticket_type_id, + "contacts": convert_and_respect_annotation_metadata( + object_=contacts, annotation=typing.Sequence[CreateTicketRequestContactsItem], direction="write" + ), + "conversation_to_link_id": conversation_to_link_id, + "company_id": company_id, + "created_at": created_at, + "assignment": convert_and_respect_annotation_metadata( + object_=assignment, annotation=CreateTicketRequestAssignment, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Ticket], + construct_type( + type_=typing.Optional[Ticket], # 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 enqueue_create_ticket( + self, + *, + ticket_type_id: str, + contacts: typing.Sequence[CreateTicketRequestContactsItem], + skip_notifications: typing.Optional[bool] = OMIT, + conversation_to_link_id: typing.Optional[str] = OMIT, + company_id: typing.Optional[str] = OMIT, + created_at: typing.Optional[int] = OMIT, + assignment: typing.Optional[CreateTicketRequestAssignment] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Jobs]: + """ + Enqueues ticket creation for asynchronous processing, returning if the job was enqueued successfully to be processed. We attempt to perform a best-effort validation on inputs before tasks are enqueued. If the given parameters are incorrect, we won't enqueue the job. + + Parameters + ---------- + ticket_type_id : str + The ID of the type of ticket you want to create + + contacts : typing.Sequence[CreateTicketRequestContactsItem] + The list of contacts (users or leads) affected by this ticket. Currently only one is allowed + + skip_notifications : typing.Optional[bool] + Option to disable notifications when a Ticket is created. + + conversation_to_link_id : typing.Optional[str] + The ID of the conversation you want to link to the ticket. Here are the valid ways of linking two tickets: + - conversation | back-office ticket + - customer tickets | non-shared back-office ticket + - conversation | tracker ticket + - customer ticket | tracker ticket + + company_id : typing.Optional[str] + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom + + created_at : typing.Optional[int] + The time the ticket was created. If not provided, the current time will be used. + + assignment : typing.Optional[CreateTicketRequestAssignment] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Jobs] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "tickets/enqueue", + method="POST", + json={ + "skip_notifications": skip_notifications, + "ticket_type_id": ticket_type_id, + "contacts": convert_and_respect_annotation_metadata( + object_=contacts, annotation=typing.Sequence[CreateTicketRequestContactsItem], direction="write" + ), + "conversation_to_link_id": conversation_to_link_id, + "company_id": company_id, + "created_at": created_at, + "assignment": convert_and_respect_annotation_metadata( + object_=assignment, annotation=CreateTicketRequestAssignment, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Jobs, + construct_type( + type_=Jobs, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 get( + self, ticket_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[typing.Optional[Ticket]]: + """ + You can fetch the details of a single ticket. + + Parameters + ---------- + ticket_id : str + The unique identifier for the ticket which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[Ticket]] + Ticket found + """ + _response = await self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(ticket_id)}", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Ticket], + construct_type( + type_=typing.Optional[Ticket], # 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, + ticket_id: str, + *, + ticket_attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + ticket_state_id: typing.Optional[str] = OMIT, + company_id: typing.Optional[str] = OMIT, + open: typing.Optional[bool] = OMIT, + is_shared: typing.Optional[bool] = OMIT, + snoozed_until: typing.Optional[int] = OMIT, + admin_id: typing.Optional[int] = OMIT, + assignee_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[typing.Optional[Ticket]]: + """ + You can update a ticket. + + Parameters + ---------- + ticket_id : str + The unique identifier for the ticket which is given by Intercom + + ticket_attributes : typing.Optional[typing.Dict[str, typing.Any]] + The attributes set on the ticket. + + ticket_state_id : typing.Optional[str] + The ID of the ticket state associated with the ticket type. + + company_id : typing.Optional[str] + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom. Set to nil to remove company. + + open : typing.Optional[bool] + Specify if a ticket is open. Set to false to close a ticket. Closing a ticket will also unsnooze it. + + is_shared : typing.Optional[bool] + Specify whether the ticket is visible to users. + + snoozed_until : typing.Optional[int] + The time you want the ticket to reopen. + + admin_id : typing.Optional[int] + The ID of the admin performing ticket update. Needed for workflows execution and attributing actions to specific admins. + + assignee_id : typing.Optional[str] + The ID of the admin or team to which the ticket is assigned. Set this 0 to unassign it. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[Ticket]] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(ticket_id)}", + method="PUT", + json={ + "ticket_attributes": ticket_attributes, + "ticket_state_id": ticket_state_id, + "company_id": company_id, + "open": open, + "is_shared": is_shared, + "snoozed_until": snoozed_until, + "admin_id": admin_id, + "assignee_id": assignee_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Ticket], + construct_type( + type_=typing.Optional[Ticket], # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 delete_ticket( + self, ticket_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DeleteTicketResponse]: + """ + You can delete a ticket using the Intercom provided ID. + + Parameters + ---------- + ticket_id : str + The unique identifier for the ticket which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeleteTicketResponse] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(ticket_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteTicketResponse, + construct_type( + type_=DeleteTicketResponse, # 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 search( + self, + *, + query: SearchRequestQuery, + pagination: typing.Optional[StartingAfterPaging] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[typing.Optional[Ticket], TicketList]: + """ + You can search for multiple tickets by the value of their attributes in order to fetch exactly which ones you want. + + To search for tickets, you send a `POST` request to `https://api.intercom.io/tickets/search`. + + This will accept a query object in the body which will define your filters. + {% 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 `20` 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 %} + + ### 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 multiples 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 + + ### Accepted Fields + + Most keys listed as part of the Ticket 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 `"foobar"`). + The `source.body` field is unique as the search will not be performed against the entire value, but instead against every element of the value separately. For example, when searching for a conversation with a `"I need support"` body - the query should contain a `=` operator with the value `"support"` for such conversation to be returned. A query with a `=` operator and a `"need support"` value will not yield a result. + + | Field | Type | + | :---------------------------------------- | :--------------------------------------------------------------------------------------- | + | id | String | + | created_at | Date (UNIX timestamp) | + | updated_at | Date (UNIX timestamp) | + | title | String | + | description | String | + | category | String | + | ticket_type_id | String | + | contact_ids | String | + | teammate_ids | String | + | admin_assignee_id | String | + | team_assignee_id | String | + | open | Boolean | + | state | String | + | snoozed_until | Date (UNIX timestamp) | + | ticket_attribute.{id} | String or Boolean or Date (UNIX timestamp) or Float or Integer | + + {% admonition type="info" name="Searching by Category" %} + When searching for tickets by the **`category`** field, specific terms must be used instead of the category names: + * For **Customer** category tickets, use the term `request`. + * For **Back-office** category tickets, use the term `task`. + * For **Tracker** category tickets, use the term `tracker`. + {% /admonition %} + + ### Accepted Operators + + {% admonition type="info" name="Searching based on `created_at`" %} + You may 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 Shortcut for `OR` queries Values most be in Array | + | NIN | All | Not In Shortcut for `OR !` queries Values must be in Array | + | > | Integer Date (UNIX Timestamp) | Greater (or equal) than | + | < | Integer Date (UNIX Timestamp) | Lower (or equal) 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[typing.Optional[Ticket], TicketList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "tickets/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( + TicketList, + construct_type( + type_=TicketList, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.tickets + _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) + _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) diff --git a/src/intercom/tickets/types/__init__.py b/src/intercom/tickets/types/__init__.py new file mode 100644 index 00000000..846590d3 --- /dev/null +++ b/src/intercom/tickets/types/__init__.py @@ -0,0 +1,98 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .delete_ticket_response import DeleteTicketResponse + from .ticket import Ticket + from .ticket_category import TicketCategory + from .ticket_contacts import TicketContacts + from .ticket_part import TicketPart + from .ticket_part_previous_ticket_state import TicketPartPreviousTicketState + from .ticket_part_ticket_state import TicketPartTicketState + from .ticket_part_updated_attribute_data import TicketPartUpdatedAttributeData + from .ticket_part_updated_attribute_data_attribute import TicketPartUpdatedAttributeDataAttribute + from .ticket_part_updated_attribute_data_value import TicketPartUpdatedAttributeDataValue + from .ticket_part_updated_attribute_data_value_id import TicketPartUpdatedAttributeDataValueId + from .ticket_part_updated_attribute_data_value_label import TicketPartUpdatedAttributeDataValueLabel + from .ticket_state import TicketState + from .ticket_state_category import TicketStateCategory + from .ticket_state_detailed import TicketStateDetailed + from .ticket_state_detailed_category import TicketStateDetailedCategory + from .ticket_state_detailed_ticket_types import TicketStateDetailedTicketTypes + from .ticket_type import TicketType + from .ticket_type_category import TicketTypeCategory + from .ticket_type_ticket_states import TicketTypeTicketStates + from .tickets_reply_request_body import TicketsReplyRequestBody +_dynamic_imports: typing.Dict[str, str] = { + "DeleteTicketResponse": ".delete_ticket_response", + "Ticket": ".ticket", + "TicketCategory": ".ticket_category", + "TicketContacts": ".ticket_contacts", + "TicketPart": ".ticket_part", + "TicketPartPreviousTicketState": ".ticket_part_previous_ticket_state", + "TicketPartTicketState": ".ticket_part_ticket_state", + "TicketPartUpdatedAttributeData": ".ticket_part_updated_attribute_data", + "TicketPartUpdatedAttributeDataAttribute": ".ticket_part_updated_attribute_data_attribute", + "TicketPartUpdatedAttributeDataValue": ".ticket_part_updated_attribute_data_value", + "TicketPartUpdatedAttributeDataValueId": ".ticket_part_updated_attribute_data_value_id", + "TicketPartUpdatedAttributeDataValueLabel": ".ticket_part_updated_attribute_data_value_label", + "TicketState": ".ticket_state", + "TicketStateCategory": ".ticket_state_category", + "TicketStateDetailed": ".ticket_state_detailed", + "TicketStateDetailedCategory": ".ticket_state_detailed_category", + "TicketStateDetailedTicketTypes": ".ticket_state_detailed_ticket_types", + "TicketType": ".ticket_type", + "TicketTypeCategory": ".ticket_type_category", + "TicketTypeTicketStates": ".ticket_type_ticket_states", + "TicketsReplyRequestBody": ".tickets_reply_request_body", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "DeleteTicketResponse", + "Ticket", + "TicketCategory", + "TicketContacts", + "TicketPart", + "TicketPartPreviousTicketState", + "TicketPartTicketState", + "TicketPartUpdatedAttributeData", + "TicketPartUpdatedAttributeDataAttribute", + "TicketPartUpdatedAttributeDataValue", + "TicketPartUpdatedAttributeDataValueId", + "TicketPartUpdatedAttributeDataValueLabel", + "TicketState", + "TicketStateCategory", + "TicketStateDetailed", + "TicketStateDetailedCategory", + "TicketStateDetailedTicketTypes", + "TicketType", + "TicketTypeCategory", + "TicketTypeTicketStates", + "TicketsReplyRequestBody", +] diff --git a/src/intercom/tickets/types/delete_ticket_response.py b/src/intercom/tickets/types/delete_ticket_response.py new file mode 100644 index 00000000..4b748b17 --- /dev/null +++ b/src/intercom/tickets/types/delete_ticket_response.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class DeleteTicketResponse(UncheckedBaseModel): + """ + Response when a ticket is deleted. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the ticket. + """ + + object: typing.Optional[typing.Literal["ticket"]] = pydantic.Field(default=None) + """ + always ticket + """ + + deleted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the ticket is deleted or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/tickets/types/ticket.py b/src/intercom/tickets/types/ticket.py new file mode 100644 index 00000000..eca715f4 --- /dev/null +++ b/src/intercom/tickets/types/ticket.py @@ -0,0 +1,90 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.linked_object_list import LinkedObjectList +from ...types.ticket_custom_attributes import TicketCustomAttributes +from ...types.ticket_parts import TicketParts +from .ticket_category import TicketCategory +from .ticket_contacts import TicketContacts +from .ticket_state import TicketState +from .ticket_type import TicketType + + +class Ticket(UncheckedBaseModel): + """ + Tickets are how you track requests from your users. + """ + + type: typing.Optional[typing.Literal["ticket"]] = pydantic.Field(default=None) + """ + Always ticket + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the ticket which is given by Intercom. + """ + + ticket_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the Ticket used in the Intercom Inbox and Messenger. Do not use ticket_id for API queries. + """ + + category: typing.Optional[TicketCategory] = pydantic.Field(default=None) + """ + Category of the Ticket. + """ + + ticket_attributes: typing.Optional[TicketCustomAttributes] = None + ticket_state: typing.Optional[TicketState] = None + ticket_type: typing.Optional[TicketType] = None + contacts: typing.Optional[TicketContacts] = None + admin_assignee_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the admin assigned to the ticket. + """ + + team_assignee_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the team assigned to the ticket. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the ticket was created as a UTC Unix timestamp. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The last time the ticket was updated as a UTC Unix timestamp. + """ + + open: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether or not the ticket is open. If false, the ticket is closed. + """ + + snoozed_until: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the ticket will be snoozed until as a UTC Unix timestamp. If null, the ticket is not currently snoozed. + """ + + linked_objects: typing.Optional[LinkedObjectList] = None + ticket_parts: typing.Optional[TicketParts] = None + is_shared: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether or not the ticket is shared with the customer. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/tickets/types/ticket_category.py b/src/intercom/tickets/types/ticket_category.py new file mode 100644 index 00000000..0bfaf71b --- /dev/null +++ b/src/intercom/tickets/types/ticket_category.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketCategory = typing.Union[typing.Literal["Customer", "Back-office", "Tracker"], typing.Any] diff --git a/src/intercom/tickets/types/ticket_contacts.py b/src/intercom/tickets/types/ticket_contacts.py new file mode 100644 index 00000000..af0454d4 --- /dev/null +++ b/src/intercom/tickets/types/ticket_contacts.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.contact_reference import ContactReference + + +class TicketContacts(UncheckedBaseModel): + """ + The list of contacts affected by a ticket. + """ + + type: typing.Optional[typing.Literal["contact.list"]] = pydantic.Field(default=None) + """ + always contact.list + """ + + contacts: typing.Optional[typing.List[ContactReference]] = pydantic.Field(default=None) + """ + The list of contacts affected by this ticket. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/tickets/types/ticket_part.py b/src/intercom/tickets/types/ticket_part.py new file mode 100644 index 00000000..c6056e28 --- /dev/null +++ b/src/intercom/tickets/types/ticket_part.py @@ -0,0 +1,99 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.part_attachment import PartAttachment +from ...types.reference import Reference +from ...types.ticket_part_author import TicketPartAuthor +from .ticket_part_previous_ticket_state import TicketPartPreviousTicketState +from .ticket_part_ticket_state import TicketPartTicketState +from .ticket_part_updated_attribute_data import TicketPartUpdatedAttributeData + + +class TicketPart(UncheckedBaseModel): + """ + A Ticket Part represents a message in the ticket. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + Always ticket_part + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the ticket part. + """ + + part_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of ticket part. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The message body, which may contain HTML. + """ + + previous_ticket_state: typing.Optional[TicketPartPreviousTicketState] = pydantic.Field(default=None) + """ + The previous state of the ticket. + """ + + ticket_state: typing.Optional[TicketPartTicketState] = pydantic.Field(default=None) + """ + The state of the ticket. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the ticket part was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The last time the ticket part was updated. + """ + + assigned_to: typing.Optional[Reference] = pydantic.Field(default=None) + """ + The id of the admin that was assigned the ticket by this ticket_part (null if there has been no change in assignment.) + """ + + author: typing.Optional[TicketPartAuthor] = None + attachments: typing.Optional[typing.List[PartAttachment]] = pydantic.Field(default=None) + """ + A list of attachments for the part. + """ + + external_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The external id of the ticket part + """ + + redacted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether or not the ticket part has been redacted. + """ + + app_package_code: typing.Optional[str] = pydantic.Field(default=None) + """ + The app package code if this part was created via API. Note this field won't show if the part was not created via API. + """ + + updated_attribute_data: typing.Optional[TicketPartUpdatedAttributeData] = pydantic.Field(default=None) + """ + The updated attribute data of the ticket part. Only present for attribute update parts. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/tickets/types/ticket_part_previous_ticket_state.py b/src/intercom/tickets/types/ticket_part_previous_ticket_state.py new file mode 100644 index 00000000..650c92d9 --- /dev/null +++ b/src/intercom/tickets/types/ticket_part_previous_ticket_state.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketPartPreviousTicketState = typing.Union[ + typing.Literal["submitted", "in_progress", "waiting_on_customer", "resolved"], typing.Any +] diff --git a/src/intercom/tickets/types/ticket_part_ticket_state.py b/src/intercom/tickets/types/ticket_part_ticket_state.py new file mode 100644 index 00000000..c430063e --- /dev/null +++ b/src/intercom/tickets/types/ticket_part_ticket_state.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketPartTicketState = typing.Union[ + typing.Literal["submitted", "in_progress", "waiting_on_customer", "resolved"], typing.Any +] diff --git a/src/intercom/tickets/types/ticket_part_updated_attribute_data.py b/src/intercom/tickets/types/ticket_part_updated_attribute_data.py new file mode 100644 index 00000000..42980c0d --- /dev/null +++ b/src/intercom/tickets/types/ticket_part_updated_attribute_data.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .ticket_part_updated_attribute_data_attribute import TicketPartUpdatedAttributeDataAttribute +from .ticket_part_updated_attribute_data_value import TicketPartUpdatedAttributeDataValue + + +class TicketPartUpdatedAttributeData(UncheckedBaseModel): + """ + The updated attribute data of the ticket part. Only present for attribute update parts. + """ + + attribute: TicketPartUpdatedAttributeDataAttribute = pydantic.Field() + """ + Information about the attribute that was updated. + """ + + value: TicketPartUpdatedAttributeDataValue = pydantic.Field() + """ + The new value of the attribute. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/tickets/types/ticket_part_updated_attribute_data_attribute.py b/src/intercom/tickets/types/ticket_part_updated_attribute_data_attribute.py new file mode 100644 index 00000000..b7bd91ef --- /dev/null +++ b/src/intercom/tickets/types/ticket_part_updated_attribute_data_attribute.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class TicketPartUpdatedAttributeDataAttribute(UncheckedBaseModel): + """ + Information about the attribute that was updated. + """ + + type: typing.Literal["attribute"] = pydantic.Field(default="attribute") + """ + The type of the object. Always 'attribute'. + """ + + id: str = pydantic.Field() + """ + The unique identifier of the attribute. + """ + + label: str = pydantic.Field() + """ + The human-readable name of the attribute. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/tickets/types/ticket_part_updated_attribute_data_value.py b/src/intercom/tickets/types/ticket_part_updated_attribute_data_value.py new file mode 100644 index 00000000..9ea5c010 --- /dev/null +++ b/src/intercom/tickets/types/ticket_part_updated_attribute_data_value.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .ticket_part_updated_attribute_data_value_id import TicketPartUpdatedAttributeDataValueId +from .ticket_part_updated_attribute_data_value_label import TicketPartUpdatedAttributeDataValueLabel + + +class TicketPartUpdatedAttributeDataValue(UncheckedBaseModel): + """ + The new value of the attribute. + """ + + type: typing.Literal["value"] = pydantic.Field(default="value") + """ + The type of the object. Always 'value'. + """ + + id: TicketPartUpdatedAttributeDataValueId + label: TicketPartUpdatedAttributeDataValueLabel + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/tickets/types/ticket_part_updated_attribute_data_value_id.py b/src/intercom/tickets/types/ticket_part_updated_attribute_data_value_id.py new file mode 100644 index 00000000..01d2f9d1 --- /dev/null +++ b/src/intercom/tickets/types/ticket_part_updated_attribute_data_value_id.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketPartUpdatedAttributeDataValueId = typing.Union[typing.Optional[str], typing.List[int]] diff --git a/src/intercom/tickets/types/ticket_part_updated_attribute_data_value_label.py b/src/intercom/tickets/types/ticket_part_updated_attribute_data_value_label.py new file mode 100644 index 00000000..4026e8af --- /dev/null +++ b/src/intercom/tickets/types/ticket_part_updated_attribute_data_value_label.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketPartUpdatedAttributeDataValueLabel = typing.Union[str, typing.List[str]] diff --git a/src/intercom/tickets/types/ticket_state.py b/src/intercom/tickets/types/ticket_state.py new file mode 100644 index 00000000..6beb7dd2 --- /dev/null +++ b/src/intercom/tickets/types/ticket_state.py @@ -0,0 +1,48 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .ticket_state_category import TicketStateCategory + + +class TicketState(UncheckedBaseModel): + """ + A ticket state, used to define the state of a ticket. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `ticket_state`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the ticket state + """ + + category: typing.Optional[TicketStateCategory] = pydantic.Field(default=None) + """ + The category of the ticket state + """ + + internal_label: typing.Optional[str] = pydantic.Field(default=None) + """ + The state the ticket is currently in, in a human readable form - visible in Intercom + """ + + external_label: typing.Optional[str] = pydantic.Field(default=None) + """ + The state the ticket is currently in, in a human readable form - visible to customers, in the messenger, email and tickets portal. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/tickets/types/ticket_state_category.py b/src/intercom/tickets/types/ticket_state_category.py new file mode 100644 index 00000000..d53c56a7 --- /dev/null +++ b/src/intercom/tickets/types/ticket_state_category.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketStateCategory = typing.Union[ + typing.Literal["submitted", "in_progress", "waiting_on_customer", "resolved"], typing.Any +] diff --git a/src/intercom/tickets/types/ticket_state_detailed.py b/src/intercom/tickets/types/ticket_state_detailed.py new file mode 100644 index 00000000..973767f8 --- /dev/null +++ b/src/intercom/tickets/types/ticket_state_detailed.py @@ -0,0 +1,59 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .ticket_state_detailed_category import TicketStateDetailedCategory +from .ticket_state_detailed_ticket_types import TicketStateDetailedTicketTypes + + +class TicketStateDetailed(UncheckedBaseModel): + """ + A ticket state, used to define the state of a ticket. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `ticket_state`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the ticket state + """ + + category: typing.Optional[TicketStateDetailedCategory] = pydantic.Field(default=None) + """ + The category of the ticket state + """ + + internal_label: typing.Optional[str] = pydantic.Field(default=None) + """ + The state the ticket is currently in, in a human readable form - visible in Intercom + """ + + external_label: typing.Optional[str] = pydantic.Field(default=None) + """ + The state the ticket is currently in, in a human readable form - visible to customers, in the messenger, email and tickets portal. + """ + + archived: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the ticket state is archived + """ + + ticket_types: typing.Optional[TicketStateDetailedTicketTypes] = pydantic.Field(default=None) + """ + A list of ticket types associated with a given ticket state. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/tickets/types/ticket_state_detailed_category.py b/src/intercom/tickets/types/ticket_state_detailed_category.py new file mode 100644 index 00000000..d8426ecc --- /dev/null +++ b/src/intercom/tickets/types/ticket_state_detailed_category.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketStateDetailedCategory = typing.Union[ + typing.Literal["submitted", "in_progress", "waiting_on_customer", "resolved"], typing.Any +] diff --git a/src/intercom/tickets/types/ticket_state_detailed_ticket_types.py b/src/intercom/tickets/types/ticket_state_detailed_ticket_types.py new file mode 100644 index 00000000..4a5f1435 --- /dev/null +++ b/src/intercom/tickets/types/ticket_state_detailed_ticket_types.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .ticket_type import TicketType + + +class TicketStateDetailedTicketTypes(UncheckedBaseModel): + """ + A list of ticket types associated with a given ticket state. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `list`. + """ + + data: typing.Optional[typing.List[typing.Optional[TicketType]]] = pydantic.Field(default=None) + """ + A list of ticket type attributes associated with a given ticket type. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/tickets/types/ticket_type.py b/src/intercom/tickets/types/ticket_type.py new file mode 100644 index 00000000..fef0d5a1 --- /dev/null +++ b/src/intercom/tickets/types/ticket_type.py @@ -0,0 +1,81 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ...types.ticket_type_attribute_list import TicketTypeAttributeList +from .ticket_type_category import TicketTypeCategory +from .ticket_type_ticket_states import TicketTypeTicketStates + + +class TicketType(UncheckedBaseModel): + """ + A ticket type, used to define the data fields to be captured in a ticket. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `ticket_type`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the ticket type. + """ + + category: typing.Optional[TicketTypeCategory] = pydantic.Field(default=None) + """ + Category of the Ticket Type. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the ticket type + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + The description of the ticket type + """ + + icon: typing.Optional[str] = pydantic.Field(default=None) + """ + The icon of the ticket type + """ + + workspace_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the workspace that the ticket type belongs to. + """ + + ticket_type_attributes: typing.Optional[TicketTypeAttributeList] = None + ticket_states: typing.Optional[TicketTypeTicketStates] = pydantic.Field(default=None) + """ + A list of ticket states associated with a given ticket type. + """ + + archived: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the ticket type is archived or not. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The date and time the ticket type was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The date and time the ticket type was last updated. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/tickets/types/ticket_type_category.py b/src/intercom/tickets/types/ticket_type_category.py new file mode 100644 index 00000000..afbe5d6b --- /dev/null +++ b/src/intercom/tickets/types/ticket_type_category.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketTypeCategory = typing.Union[typing.Literal["Customer", "Back-office", "Tracker"], typing.Any] diff --git a/src/intercom/tickets/types/ticket_type_ticket_states.py b/src/intercom/tickets/types/ticket_type_ticket_states.py new file mode 100644 index 00000000..73d3d3c5 --- /dev/null +++ b/src/intercom/tickets/types/ticket_type_ticket_states.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .ticket_state import TicketState + + +class TicketTypeTicketStates(UncheckedBaseModel): + """ + A list of ticket states associated with a given ticket type. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `list`. + """ + + data: typing.Optional[typing.List[typing.Optional[TicketState]]] = pydantic.Field(default=None) + """ + A list of ticket states associated with a given ticket type. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/tickets/types/tickets_reply_request_body.py b/src/intercom/tickets/types/tickets_reply_request_body.py new file mode 100644 index 00000000..4eea13db --- /dev/null +++ b/src/intercom/tickets/types/tickets_reply_request_body.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...types.admin_reply_ticket_request import AdminReplyTicketRequest +from ...types.contact_reply_ticket_request import ContactReplyTicketRequest + +TicketsReplyRequestBody = typing.Union[ContactReplyTicketRequest, AdminReplyTicketRequest] diff --git a/src/intercom/types/__init__.py b/src/intercom/types/__init__.py new file mode 100644 index 00000000..7e922d52 --- /dev/null +++ b/src/intercom/types/__init__.py @@ -0,0 +1,811 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .activity_log import ActivityLog + from .activity_log_activity_type import ActivityLogActivityType + from .activity_log_list import ActivityLogList + from .activity_log_metadata import ActivityLogMetadata + from .activity_log_metadata_team import ActivityLogMetadataTeam + from .activity_log_performed_by import ActivityLogPerformedBy + from .addressable_list import AddressableList + from .admin_list import AdminList + from .admin_priority_level import AdminPriorityLevel + from .admin_reply_conversation_request import AdminReplyConversationRequest + from .admin_reply_conversation_request_message_type import AdminReplyConversationRequestMessageType + from .admin_reply_ticket_request import AdminReplyTicketRequest + from .admin_reply_ticket_request_message_type import AdminReplyTicketRequestMessageType + from .admin_reply_ticket_request_reply_options_item import AdminReplyTicketRequestReplyOptionsItem + from .admin_with_app import AdminWithApp + from .admin_with_app_avatar import AdminWithAppAvatar + from .app import App + from .article_content import ArticleContent + from .article_content_state import ArticleContentState + from .article_list import ArticleList + from .article_statistics import ArticleStatistics + from .article_translated_content import ArticleTranslatedContent + from .assign_conversation_request import AssignConversationRequest + from .assign_conversation_request_type import AssignConversationRequestType + from .away_status_reason import AwayStatusReason + from .call_list import CallList + from .close_conversation_request import CloseConversationRequest + from .collection_list import CollectionList + from .company_attached_contacts import CompanyAttachedContacts + from .company_attached_segments import CompanyAttachedSegments + from .company_data import CompanyData + from .company_list import CompanyList + from .company_scroll import CompanyScroll + from .contact_archived import ContactArchived + from .contact_attached_companies import ContactAttachedCompanies + from .contact_blocked import ContactBlocked + from .contact_companies import ContactCompanies + from .contact_deleted import ContactDeleted + from .contact_list import ContactList + from .contact_location import ContactLocation + from .contact_notes import ContactNotes + from .contact_reference import ContactReference + from .contact_reply_base_request import ContactReplyBaseRequest + from .contact_reply_base_request_reply_options_item import ContactReplyBaseRequestReplyOptionsItem + from .contact_reply_conversation_request import ContactReplyConversationRequest + from .contact_reply_email_request import ContactReplyEmailRequest + from .contact_reply_intercom_user_id_request import ContactReplyIntercomUserIdRequest + from .contact_reply_ticket_email_request import ContactReplyTicketEmailRequest + from .contact_reply_ticket_intercom_user_id_request import ContactReplyTicketIntercomUserIdRequest + from .contact_reply_ticket_request import ContactReplyTicketRequest + from .contact_reply_ticket_user_id_request import ContactReplyTicketUserIdRequest + from .contact_reply_user_id_request import ContactReplyUserIdRequest + from .contact_segments import ContactSegments + from .contact_social_profiles import ContactSocialProfiles + from .contact_subscription_types import ContactSubscriptionTypes + from .contact_tags import ContactTags + from .contact_unarchived import ContactUnarchived + from .content_sources_list import ContentSourcesList + from .conversation_attachment_files import ConversationAttachmentFiles + from .conversation_attribute_updated_by_admin import ConversationAttributeUpdatedByAdmin + from .conversation_attribute_updated_by_admin_attribute import ConversationAttributeUpdatedByAdminAttribute + from .conversation_attribute_updated_by_admin_value import ConversationAttributeUpdatedByAdminValue + from .conversation_attribute_updated_by_workflow import ConversationAttributeUpdatedByWorkflow + from .conversation_attribute_updated_by_workflow_attribute import ConversationAttributeUpdatedByWorkflowAttribute + from .conversation_attribute_updated_by_workflow_value import ConversationAttributeUpdatedByWorkflowValue + from .conversation_attribute_updated_by_workflow_workflow import ConversationAttributeUpdatedByWorkflowWorkflow + from .conversation_contacts import ConversationContacts + from .conversation_deleted import ConversationDeleted + from .conversation_first_contact_reply import ConversationFirstContactReply + from .conversation_list import ConversationList + from .conversation_part import ConversationPart + from .conversation_part_author import ConversationPartAuthor + from .conversation_part_metadata import ConversationPartMetadata + from .conversation_part_metadata_quick_reply_options_item import ConversationPartMetadataQuickReplyOptionsItem + from .conversation_part_state import ConversationPartState + from .conversation_parts import ConversationParts + from .conversation_rating import ConversationRating + from .conversation_response_time import ConversationResponseTime + from .conversation_source import ConversationSource + from .conversation_source_type import ConversationSourceType + from .conversation_statistics import ConversationStatistics + from .conversation_teammates import ConversationTeammates + from .create_article_request import CreateArticleRequest + from .create_article_request_parent_type import CreateArticleRequestParentType + from .create_article_request_state import CreateArticleRequestState + from .create_contact_request import CreateContactRequest + from .create_contact_request_two import CreateContactRequestTwo + from .create_contact_request_with_email import CreateContactRequestWithEmail + from .create_contact_request_with_external_id import CreateContactRequestWithExternalId + from .create_contact_request_with_role import CreateContactRequestWithRole + from .create_data_attribute_request import CreateDataAttributeRequest + from .create_data_attribute_request_one import CreateDataAttributeRequestOne + from .create_data_attribute_request_one_data_type import CreateDataAttributeRequestOneDataType + from .create_data_attribute_request_options import CreateDataAttributeRequestOptions + from .create_data_attribute_request_options_options_item import CreateDataAttributeRequestOptionsOptionsItem + from .create_data_event_request import CreateDataEventRequest + from .create_data_event_request_two import CreateDataEventRequestTwo + from .create_data_event_request_with_email import CreateDataEventRequestWithEmail + from .create_data_event_request_with_id import CreateDataEventRequestWithId + from .create_data_event_request_with_user_id import CreateDataEventRequestWithUserId + from .create_internal_article_request import CreateInternalArticleRequest + from .create_message_request import CreateMessageRequest, CreateMessageRequest_Email, CreateMessageRequest_Inapp + from .create_message_request_from import CreateMessageRequestFrom + from .create_message_request_to import CreateMessageRequestTo + from .create_message_request_type import CreateMessageRequestType + from .create_message_request_with_email import CreateMessageRequestWithEmail + from .create_message_request_with_inapp import CreateMessageRequestWithInapp + from .create_or_update_company_request import CreateOrUpdateCompanyRequest + from .create_or_update_tag_request import CreateOrUpdateTagRequest + from .create_phone_switch_request import CreatePhoneSwitchRequest + from .create_ticket_reply_with_comment_request import CreateTicketReplyWithCommentRequest + from .create_ticket_request_assignment import CreateTicketRequestAssignment + from .create_ticket_request_body import CreateTicketRequestBody + from .create_ticket_request_contacts_item import CreateTicketRequestContactsItem + from .create_ticket_request_contacts_item_email import CreateTicketRequestContactsItemEmail + from .create_ticket_request_contacts_item_external_id import CreateTicketRequestContactsItemExternalId + from .create_ticket_request_contacts_item_id import CreateTicketRequestContactsItemId + from .create_ticket_type_request import CreateTicketTypeRequest + from .create_ticket_type_request_category import CreateTicketTypeRequestCategory + from .cursor_pages import CursorPages + from .custom_action_finished import CustomActionFinished + from .custom_action_finished_action import CustomActionFinishedAction + from .custom_action_finished_action_result import CustomActionFinishedActionResult + from .custom_action_started import CustomActionStarted + from .custom_action_started_action import CustomActionStartedAction + from .custom_attributes import CustomAttributes + from .custom_attributes_value import CustomAttributesValue + from .custom_channel_attribute import CustomChannelAttribute + from .custom_channel_base_event import CustomChannelBaseEvent + from .custom_channel_contact import CustomChannelContact + from .custom_channel_contact_type import CustomChannelContactType + from .custom_channel_notification_response import CustomChannelNotificationResponse + from .custom_object_instance_deleted import CustomObjectInstanceDeleted + from .custom_object_instance_list import CustomObjectInstanceList + from .customer_request import CustomerRequest + from .customer_request_email import CustomerRequestEmail + from .customer_request_intercom_user_id import CustomerRequestIntercomUserId + from .customer_request_user_id import CustomerRequestUserId + from .data_attribute_list import DataAttributeList + from .data_event_list import DataEventList + from .data_event_list_pages import DataEventListPages + from .data_event_summary import DataEventSummary + from .data_event_summary_item import DataEventSummaryItem + from .data_export_csv import DataExportCsv + from .datetime import Datetime + from .deleted_article_object import DeletedArticleObject + from .deleted_collection_object import DeletedCollectionObject + from .deleted_company_object import DeletedCompanyObject + from .deleted_internal_article_object import DeletedInternalArticleObject + from .deleted_object import DeletedObject + from .email_address_header import EmailAddressHeader + from .email_message_metadata import EmailMessageMetadata + from .error import Error + from .error_errors_item import ErrorErrorsItem + from .event_details import EventDetails + from .file_attribute import FileAttribute + from .group_content import GroupContent + from .group_translated_content import GroupTranslatedContent + from .internal_article_list import InternalArticleList + from .linked_object import LinkedObject + from .linked_object_category import LinkedObjectCategory + from .linked_object_list import LinkedObjectList + from .linked_object_type import LinkedObjectType + from .metadata import Metadata + from .multiple_filter_search_request import MultipleFilterSearchRequest + from .multiple_filter_search_request_operator import MultipleFilterSearchRequestOperator + from .multiple_filter_search_request_value import MultipleFilterSearchRequestValue + from .news_item_request import NewsItemRequest + from .news_item_request_state import NewsItemRequestState + from .not_found_error_body import NotFoundErrorBody + from .not_found_error_body_errors_item import NotFoundErrorBodyErrorsItem + from .note_list import NoteList + from .offset_pages import OffsetPages + from .open_conversation_request import OpenConversationRequest + from .operator_workflow_event import OperatorWorkflowEvent + from .operator_workflow_event_event import OperatorWorkflowEventEvent + from .operator_workflow_event_workflow import OperatorWorkflowEventWorkflow + from .pages_link import PagesLink + from .paginated_response import PaginatedResponse + from .paginated_response_data_item import ( + PaginatedResponseDataItem, + PaginatedResponseDataItem_NewsItem, + PaginatedResponseDataItem_Newsfeed, + ) + from .paginated_response_type import PaginatedResponseType + from .part_attachment import PartAttachment + from .phone_switch import PhoneSwitch + from .quick_reply_option import QuickReplyOption + from .recipient import Recipient + from .recipient_type import RecipientType + from .redact_conversation_request import ( + RedactConversationRequest, + RedactConversationRequest_ConversationPart, + RedactConversationRequest_Source, + ) + from .redact_conversation_request_conversation_part import RedactConversationRequestConversationPart + from .redact_conversation_request_source import RedactConversationRequestSource + from .reference import Reference + from .reply_conversation_request import ReplyConversationRequest + from .search_request import SearchRequest + from .search_request_query import SearchRequestQuery + from .segment_list import SegmentList + from .single_filter_search_request import SingleFilterSearchRequest + from .single_filter_search_request_operator import SingleFilterSearchRequestOperator + from .single_filter_search_request_value import SingleFilterSearchRequestValue + from .single_filter_search_request_value_two_item import SingleFilterSearchRequestValueTwoItem + from .sla_applied import SlaApplied + from .sla_applied_sla_status import SlaAppliedSlaStatus + from .snooze_conversation_request import SnoozeConversationRequest + from .social_profile import SocialProfile + from .starting_after_paging import StartingAfterPaging + from .subscription_type_list import SubscriptionTypeList + from .tag_company_request import TagCompanyRequest + from .tag_company_request_companies_item import TagCompanyRequestCompaniesItem + from .tag_list import TagList + from .tag_multiple_users_request import TagMultipleUsersRequest + from .tag_multiple_users_request_users_item import TagMultipleUsersRequestUsersItem + from .tags import Tags + from .team_list import TeamList + from .team_priority_level import TeamPriorityLevel + from .ticket_custom_attributes import TicketCustomAttributes + from .ticket_list import TicketList + from .ticket_part_author import TicketPartAuthor + from .ticket_part_author_type import TicketPartAuthorType + from .ticket_parts import TicketParts + from .ticket_reply import TicketReply + from .ticket_reply_part_type import TicketReplyPartType + from .ticket_request_custom_attributes import TicketRequestCustomAttributes + from .ticket_state_list import TicketStateList + from .ticket_type_attribute import TicketTypeAttribute + from .ticket_type_attribute_data_type import TicketTypeAttributeDataType + from .ticket_type_attribute_list import TicketTypeAttributeList + from .ticket_type_list import TicketTypeList + from .translation import Translation + from .untag_company_request import UntagCompanyRequest + from .untag_company_request_companies_item import UntagCompanyRequestCompaniesItem + from .update_article_request_body import UpdateArticleRequestBody + from .update_article_request_body_parent_type import UpdateArticleRequestBodyParentType + from .update_company_request_body import UpdateCompanyRequestBody + from .update_data_attribute_request_body import UpdateDataAttributeRequestBody + from .update_data_attribute_request_options import UpdateDataAttributeRequestOptions + from .update_data_attribute_request_options_options_item import UpdateDataAttributeRequestOptionsOptionsItem + from .update_visitor_request import UpdateVisitorRequest + from .update_visitor_request_one import UpdateVisitorRequestOne + from .update_visitor_request_with_id import UpdateVisitorRequestWithId + from .update_visitor_request_with_user_id import UpdateVisitorRequestWithUserId + from .visitor import Visitor + from .visitor_avatar import VisitorAvatar + from .visitor_companies import VisitorCompanies + from .visitor_deleted_object import VisitorDeletedObject + from .visitor_location_data import VisitorLocationData + from .visitor_segments import VisitorSegments + from .visitor_social_profiles import VisitorSocialProfiles + from .visitor_tags import VisitorTags + from .visitor_tags_tags_item import VisitorTagsTagsItem + from .whatsapp_message_status_list import WhatsappMessageStatusList + from .whatsapp_message_status_list_events_item import WhatsappMessageStatusListEventsItem + from .whatsapp_message_status_list_events_item_status import WhatsappMessageStatusListEventsItemStatus + from .whatsapp_message_status_list_pages import WhatsappMessageStatusListPages + from .whatsapp_message_status_list_pages_next import WhatsappMessageStatusListPagesNext +_dynamic_imports: typing.Dict[str, str] = { + "ActivityLog": ".activity_log", + "ActivityLogActivityType": ".activity_log_activity_type", + "ActivityLogList": ".activity_log_list", + "ActivityLogMetadata": ".activity_log_metadata", + "ActivityLogMetadataTeam": ".activity_log_metadata_team", + "ActivityLogPerformedBy": ".activity_log_performed_by", + "AddressableList": ".addressable_list", + "AdminList": ".admin_list", + "AdminPriorityLevel": ".admin_priority_level", + "AdminReplyConversationRequest": ".admin_reply_conversation_request", + "AdminReplyConversationRequestMessageType": ".admin_reply_conversation_request_message_type", + "AdminReplyTicketRequest": ".admin_reply_ticket_request", + "AdminReplyTicketRequestMessageType": ".admin_reply_ticket_request_message_type", + "AdminReplyTicketRequestReplyOptionsItem": ".admin_reply_ticket_request_reply_options_item", + "AdminWithApp": ".admin_with_app", + "AdminWithAppAvatar": ".admin_with_app_avatar", + "App": ".app", + "ArticleContent": ".article_content", + "ArticleContentState": ".article_content_state", + "ArticleList": ".article_list", + "ArticleStatistics": ".article_statistics", + "ArticleTranslatedContent": ".article_translated_content", + "AssignConversationRequest": ".assign_conversation_request", + "AssignConversationRequestType": ".assign_conversation_request_type", + "AwayStatusReason": ".away_status_reason", + "CallList": ".call_list", + "CloseConversationRequest": ".close_conversation_request", + "CollectionList": ".collection_list", + "CompanyAttachedContacts": ".company_attached_contacts", + "CompanyAttachedSegments": ".company_attached_segments", + "CompanyData": ".company_data", + "CompanyList": ".company_list", + "CompanyScroll": ".company_scroll", + "ContactArchived": ".contact_archived", + "ContactAttachedCompanies": ".contact_attached_companies", + "ContactBlocked": ".contact_blocked", + "ContactCompanies": ".contact_companies", + "ContactDeleted": ".contact_deleted", + "ContactList": ".contact_list", + "ContactLocation": ".contact_location", + "ContactNotes": ".contact_notes", + "ContactReference": ".contact_reference", + "ContactReplyBaseRequest": ".contact_reply_base_request", + "ContactReplyBaseRequestReplyOptionsItem": ".contact_reply_base_request_reply_options_item", + "ContactReplyConversationRequest": ".contact_reply_conversation_request", + "ContactReplyEmailRequest": ".contact_reply_email_request", + "ContactReplyIntercomUserIdRequest": ".contact_reply_intercom_user_id_request", + "ContactReplyTicketEmailRequest": ".contact_reply_ticket_email_request", + "ContactReplyTicketIntercomUserIdRequest": ".contact_reply_ticket_intercom_user_id_request", + "ContactReplyTicketRequest": ".contact_reply_ticket_request", + "ContactReplyTicketUserIdRequest": ".contact_reply_ticket_user_id_request", + "ContactReplyUserIdRequest": ".contact_reply_user_id_request", + "ContactSegments": ".contact_segments", + "ContactSocialProfiles": ".contact_social_profiles", + "ContactSubscriptionTypes": ".contact_subscription_types", + "ContactTags": ".contact_tags", + "ContactUnarchived": ".contact_unarchived", + "ContentSourcesList": ".content_sources_list", + "ConversationAttachmentFiles": ".conversation_attachment_files", + "ConversationAttributeUpdatedByAdmin": ".conversation_attribute_updated_by_admin", + "ConversationAttributeUpdatedByAdminAttribute": ".conversation_attribute_updated_by_admin_attribute", + "ConversationAttributeUpdatedByAdminValue": ".conversation_attribute_updated_by_admin_value", + "ConversationAttributeUpdatedByWorkflow": ".conversation_attribute_updated_by_workflow", + "ConversationAttributeUpdatedByWorkflowAttribute": ".conversation_attribute_updated_by_workflow_attribute", + "ConversationAttributeUpdatedByWorkflowValue": ".conversation_attribute_updated_by_workflow_value", + "ConversationAttributeUpdatedByWorkflowWorkflow": ".conversation_attribute_updated_by_workflow_workflow", + "ConversationContacts": ".conversation_contacts", + "ConversationDeleted": ".conversation_deleted", + "ConversationFirstContactReply": ".conversation_first_contact_reply", + "ConversationList": ".conversation_list", + "ConversationPart": ".conversation_part", + "ConversationPartAuthor": ".conversation_part_author", + "ConversationPartMetadata": ".conversation_part_metadata", + "ConversationPartMetadataQuickReplyOptionsItem": ".conversation_part_metadata_quick_reply_options_item", + "ConversationPartState": ".conversation_part_state", + "ConversationParts": ".conversation_parts", + "ConversationRating": ".conversation_rating", + "ConversationResponseTime": ".conversation_response_time", + "ConversationSource": ".conversation_source", + "ConversationSourceType": ".conversation_source_type", + "ConversationStatistics": ".conversation_statistics", + "ConversationTeammates": ".conversation_teammates", + "CreateArticleRequest": ".create_article_request", + "CreateArticleRequestParentType": ".create_article_request_parent_type", + "CreateArticleRequestState": ".create_article_request_state", + "CreateContactRequest": ".create_contact_request", + "CreateContactRequestTwo": ".create_contact_request_two", + "CreateContactRequestWithEmail": ".create_contact_request_with_email", + "CreateContactRequestWithExternalId": ".create_contact_request_with_external_id", + "CreateContactRequestWithRole": ".create_contact_request_with_role", + "CreateDataAttributeRequest": ".create_data_attribute_request", + "CreateDataAttributeRequestOne": ".create_data_attribute_request_one", + "CreateDataAttributeRequestOneDataType": ".create_data_attribute_request_one_data_type", + "CreateDataAttributeRequestOptions": ".create_data_attribute_request_options", + "CreateDataAttributeRequestOptionsOptionsItem": ".create_data_attribute_request_options_options_item", + "CreateDataEventRequest": ".create_data_event_request", + "CreateDataEventRequestTwo": ".create_data_event_request_two", + "CreateDataEventRequestWithEmail": ".create_data_event_request_with_email", + "CreateDataEventRequestWithId": ".create_data_event_request_with_id", + "CreateDataEventRequestWithUserId": ".create_data_event_request_with_user_id", + "CreateInternalArticleRequest": ".create_internal_article_request", + "CreateMessageRequest": ".create_message_request", + "CreateMessageRequestFrom": ".create_message_request_from", + "CreateMessageRequestTo": ".create_message_request_to", + "CreateMessageRequestType": ".create_message_request_type", + "CreateMessageRequestWithEmail": ".create_message_request_with_email", + "CreateMessageRequestWithInapp": ".create_message_request_with_inapp", + "CreateMessageRequest_Email": ".create_message_request", + "CreateMessageRequest_Inapp": ".create_message_request", + "CreateOrUpdateCompanyRequest": ".create_or_update_company_request", + "CreateOrUpdateTagRequest": ".create_or_update_tag_request", + "CreatePhoneSwitchRequest": ".create_phone_switch_request", + "CreateTicketReplyWithCommentRequest": ".create_ticket_reply_with_comment_request", + "CreateTicketRequestAssignment": ".create_ticket_request_assignment", + "CreateTicketRequestBody": ".create_ticket_request_body", + "CreateTicketRequestContactsItem": ".create_ticket_request_contacts_item", + "CreateTicketRequestContactsItemEmail": ".create_ticket_request_contacts_item_email", + "CreateTicketRequestContactsItemExternalId": ".create_ticket_request_contacts_item_external_id", + "CreateTicketRequestContactsItemId": ".create_ticket_request_contacts_item_id", + "CreateTicketTypeRequest": ".create_ticket_type_request", + "CreateTicketTypeRequestCategory": ".create_ticket_type_request_category", + "CursorPages": ".cursor_pages", + "CustomActionFinished": ".custom_action_finished", + "CustomActionFinishedAction": ".custom_action_finished_action", + "CustomActionFinishedActionResult": ".custom_action_finished_action_result", + "CustomActionStarted": ".custom_action_started", + "CustomActionStartedAction": ".custom_action_started_action", + "CustomAttributes": ".custom_attributes", + "CustomAttributesValue": ".custom_attributes_value", + "CustomChannelAttribute": ".custom_channel_attribute", + "CustomChannelBaseEvent": ".custom_channel_base_event", + "CustomChannelContact": ".custom_channel_contact", + "CustomChannelContactType": ".custom_channel_contact_type", + "CustomChannelNotificationResponse": ".custom_channel_notification_response", + "CustomObjectInstanceDeleted": ".custom_object_instance_deleted", + "CustomObjectInstanceList": ".custom_object_instance_list", + "CustomerRequest": ".customer_request", + "CustomerRequestEmail": ".customer_request_email", + "CustomerRequestIntercomUserId": ".customer_request_intercom_user_id", + "CustomerRequestUserId": ".customer_request_user_id", + "DataAttributeList": ".data_attribute_list", + "DataEventList": ".data_event_list", + "DataEventListPages": ".data_event_list_pages", + "DataEventSummary": ".data_event_summary", + "DataEventSummaryItem": ".data_event_summary_item", + "DataExportCsv": ".data_export_csv", + "Datetime": ".datetime", + "DeletedArticleObject": ".deleted_article_object", + "DeletedCollectionObject": ".deleted_collection_object", + "DeletedCompanyObject": ".deleted_company_object", + "DeletedInternalArticleObject": ".deleted_internal_article_object", + "DeletedObject": ".deleted_object", + "EmailAddressHeader": ".email_address_header", + "EmailMessageMetadata": ".email_message_metadata", + "Error": ".error", + "ErrorErrorsItem": ".error_errors_item", + "EventDetails": ".event_details", + "FileAttribute": ".file_attribute", + "GroupContent": ".group_content", + "GroupTranslatedContent": ".group_translated_content", + "InternalArticleList": ".internal_article_list", + "LinkedObject": ".linked_object", + "LinkedObjectCategory": ".linked_object_category", + "LinkedObjectList": ".linked_object_list", + "LinkedObjectType": ".linked_object_type", + "Metadata": ".metadata", + "MultipleFilterSearchRequest": ".multiple_filter_search_request", + "MultipleFilterSearchRequestOperator": ".multiple_filter_search_request_operator", + "MultipleFilterSearchRequestValue": ".multiple_filter_search_request_value", + "NewsItemRequest": ".news_item_request", + "NewsItemRequestState": ".news_item_request_state", + "NotFoundErrorBody": ".not_found_error_body", + "NotFoundErrorBodyErrorsItem": ".not_found_error_body_errors_item", + "NoteList": ".note_list", + "OffsetPages": ".offset_pages", + "OpenConversationRequest": ".open_conversation_request", + "OperatorWorkflowEvent": ".operator_workflow_event", + "OperatorWorkflowEventEvent": ".operator_workflow_event_event", + "OperatorWorkflowEventWorkflow": ".operator_workflow_event_workflow", + "PagesLink": ".pages_link", + "PaginatedResponse": ".paginated_response", + "PaginatedResponseDataItem": ".paginated_response_data_item", + "PaginatedResponseDataItem_NewsItem": ".paginated_response_data_item", + "PaginatedResponseDataItem_Newsfeed": ".paginated_response_data_item", + "PaginatedResponseType": ".paginated_response_type", + "PartAttachment": ".part_attachment", + "PhoneSwitch": ".phone_switch", + "QuickReplyOption": ".quick_reply_option", + "Recipient": ".recipient", + "RecipientType": ".recipient_type", + "RedactConversationRequest": ".redact_conversation_request", + "RedactConversationRequestConversationPart": ".redact_conversation_request_conversation_part", + "RedactConversationRequestSource": ".redact_conversation_request_source", + "RedactConversationRequest_ConversationPart": ".redact_conversation_request", + "RedactConversationRequest_Source": ".redact_conversation_request", + "Reference": ".reference", + "ReplyConversationRequest": ".reply_conversation_request", + "SearchRequest": ".search_request", + "SearchRequestQuery": ".search_request_query", + "SegmentList": ".segment_list", + "SingleFilterSearchRequest": ".single_filter_search_request", + "SingleFilterSearchRequestOperator": ".single_filter_search_request_operator", + "SingleFilterSearchRequestValue": ".single_filter_search_request_value", + "SingleFilterSearchRequestValueTwoItem": ".single_filter_search_request_value_two_item", + "SlaApplied": ".sla_applied", + "SlaAppliedSlaStatus": ".sla_applied_sla_status", + "SnoozeConversationRequest": ".snooze_conversation_request", + "SocialProfile": ".social_profile", + "StartingAfterPaging": ".starting_after_paging", + "SubscriptionTypeList": ".subscription_type_list", + "TagCompanyRequest": ".tag_company_request", + "TagCompanyRequestCompaniesItem": ".tag_company_request_companies_item", + "TagList": ".tag_list", + "TagMultipleUsersRequest": ".tag_multiple_users_request", + "TagMultipleUsersRequestUsersItem": ".tag_multiple_users_request_users_item", + "Tags": ".tags", + "TeamList": ".team_list", + "TeamPriorityLevel": ".team_priority_level", + "TicketCustomAttributes": ".ticket_custom_attributes", + "TicketList": ".ticket_list", + "TicketPartAuthor": ".ticket_part_author", + "TicketPartAuthorType": ".ticket_part_author_type", + "TicketParts": ".ticket_parts", + "TicketReply": ".ticket_reply", + "TicketReplyPartType": ".ticket_reply_part_type", + "TicketRequestCustomAttributes": ".ticket_request_custom_attributes", + "TicketStateList": ".ticket_state_list", + "TicketTypeAttribute": ".ticket_type_attribute", + "TicketTypeAttributeDataType": ".ticket_type_attribute_data_type", + "TicketTypeAttributeList": ".ticket_type_attribute_list", + "TicketTypeList": ".ticket_type_list", + "Translation": ".translation", + "UntagCompanyRequest": ".untag_company_request", + "UntagCompanyRequestCompaniesItem": ".untag_company_request_companies_item", + "UpdateArticleRequestBody": ".update_article_request_body", + "UpdateArticleRequestBodyParentType": ".update_article_request_body_parent_type", + "UpdateCompanyRequestBody": ".update_company_request_body", + "UpdateDataAttributeRequestBody": ".update_data_attribute_request_body", + "UpdateDataAttributeRequestOptions": ".update_data_attribute_request_options", + "UpdateDataAttributeRequestOptionsOptionsItem": ".update_data_attribute_request_options_options_item", + "UpdateVisitorRequest": ".update_visitor_request", + "UpdateVisitorRequestOne": ".update_visitor_request_one", + "UpdateVisitorRequestWithId": ".update_visitor_request_with_id", + "UpdateVisitorRequestWithUserId": ".update_visitor_request_with_user_id", + "Visitor": ".visitor", + "VisitorAvatar": ".visitor_avatar", + "VisitorCompanies": ".visitor_companies", + "VisitorDeletedObject": ".visitor_deleted_object", + "VisitorLocationData": ".visitor_location_data", + "VisitorSegments": ".visitor_segments", + "VisitorSocialProfiles": ".visitor_social_profiles", + "VisitorTags": ".visitor_tags", + "VisitorTagsTagsItem": ".visitor_tags_tags_item", + "WhatsappMessageStatusList": ".whatsapp_message_status_list", + "WhatsappMessageStatusListEventsItem": ".whatsapp_message_status_list_events_item", + "WhatsappMessageStatusListEventsItemStatus": ".whatsapp_message_status_list_events_item_status", + "WhatsappMessageStatusListPages": ".whatsapp_message_status_list_pages", + "WhatsappMessageStatusListPagesNext": ".whatsapp_message_status_list_pages_next", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "ActivityLog", + "ActivityLogActivityType", + "ActivityLogList", + "ActivityLogMetadata", + "ActivityLogMetadataTeam", + "ActivityLogPerformedBy", + "AddressableList", + "AdminList", + "AdminPriorityLevel", + "AdminReplyConversationRequest", + "AdminReplyConversationRequestMessageType", + "AdminReplyTicketRequest", + "AdminReplyTicketRequestMessageType", + "AdminReplyTicketRequestReplyOptionsItem", + "AdminWithApp", + "AdminWithAppAvatar", + "App", + "ArticleContent", + "ArticleContentState", + "ArticleList", + "ArticleStatistics", + "ArticleTranslatedContent", + "AssignConversationRequest", + "AssignConversationRequestType", + "AwayStatusReason", + "CallList", + "CloseConversationRequest", + "CollectionList", + "CompanyAttachedContacts", + "CompanyAttachedSegments", + "CompanyData", + "CompanyList", + "CompanyScroll", + "ContactArchived", + "ContactAttachedCompanies", + "ContactBlocked", + "ContactCompanies", + "ContactDeleted", + "ContactList", + "ContactLocation", + "ContactNotes", + "ContactReference", + "ContactReplyBaseRequest", + "ContactReplyBaseRequestReplyOptionsItem", + "ContactReplyConversationRequest", + "ContactReplyEmailRequest", + "ContactReplyIntercomUserIdRequest", + "ContactReplyTicketEmailRequest", + "ContactReplyTicketIntercomUserIdRequest", + "ContactReplyTicketRequest", + "ContactReplyTicketUserIdRequest", + "ContactReplyUserIdRequest", + "ContactSegments", + "ContactSocialProfiles", + "ContactSubscriptionTypes", + "ContactTags", + "ContactUnarchived", + "ContentSourcesList", + "ConversationAttachmentFiles", + "ConversationAttributeUpdatedByAdmin", + "ConversationAttributeUpdatedByAdminAttribute", + "ConversationAttributeUpdatedByAdminValue", + "ConversationAttributeUpdatedByWorkflow", + "ConversationAttributeUpdatedByWorkflowAttribute", + "ConversationAttributeUpdatedByWorkflowValue", + "ConversationAttributeUpdatedByWorkflowWorkflow", + "ConversationContacts", + "ConversationDeleted", + "ConversationFirstContactReply", + "ConversationList", + "ConversationPart", + "ConversationPartAuthor", + "ConversationPartMetadata", + "ConversationPartMetadataQuickReplyOptionsItem", + "ConversationPartState", + "ConversationParts", + "ConversationRating", + "ConversationResponseTime", + "ConversationSource", + "ConversationSourceType", + "ConversationStatistics", + "ConversationTeammates", + "CreateArticleRequest", + "CreateArticleRequestParentType", + "CreateArticleRequestState", + "CreateContactRequest", + "CreateContactRequestTwo", + "CreateContactRequestWithEmail", + "CreateContactRequestWithExternalId", + "CreateContactRequestWithRole", + "CreateDataAttributeRequest", + "CreateDataAttributeRequestOne", + "CreateDataAttributeRequestOneDataType", + "CreateDataAttributeRequestOptions", + "CreateDataAttributeRequestOptionsOptionsItem", + "CreateDataEventRequest", + "CreateDataEventRequestTwo", + "CreateDataEventRequestWithEmail", + "CreateDataEventRequestWithId", + "CreateDataEventRequestWithUserId", + "CreateInternalArticleRequest", + "CreateMessageRequest", + "CreateMessageRequestFrom", + "CreateMessageRequestTo", + "CreateMessageRequestType", + "CreateMessageRequestWithEmail", + "CreateMessageRequestWithInapp", + "CreateMessageRequest_Email", + "CreateMessageRequest_Inapp", + "CreateOrUpdateCompanyRequest", + "CreateOrUpdateTagRequest", + "CreatePhoneSwitchRequest", + "CreateTicketReplyWithCommentRequest", + "CreateTicketRequestAssignment", + "CreateTicketRequestBody", + "CreateTicketRequestContactsItem", + "CreateTicketRequestContactsItemEmail", + "CreateTicketRequestContactsItemExternalId", + "CreateTicketRequestContactsItemId", + "CreateTicketTypeRequest", + "CreateTicketTypeRequestCategory", + "CursorPages", + "CustomActionFinished", + "CustomActionFinishedAction", + "CustomActionFinishedActionResult", + "CustomActionStarted", + "CustomActionStartedAction", + "CustomAttributes", + "CustomAttributesValue", + "CustomChannelAttribute", + "CustomChannelBaseEvent", + "CustomChannelContact", + "CustomChannelContactType", + "CustomChannelNotificationResponse", + "CustomObjectInstanceDeleted", + "CustomObjectInstanceList", + "CustomerRequest", + "CustomerRequestEmail", + "CustomerRequestIntercomUserId", + "CustomerRequestUserId", + "DataAttributeList", + "DataEventList", + "DataEventListPages", + "DataEventSummary", + "DataEventSummaryItem", + "DataExportCsv", + "Datetime", + "DeletedArticleObject", + "DeletedCollectionObject", + "DeletedCompanyObject", + "DeletedInternalArticleObject", + "DeletedObject", + "EmailAddressHeader", + "EmailMessageMetadata", + "Error", + "ErrorErrorsItem", + "EventDetails", + "FileAttribute", + "GroupContent", + "GroupTranslatedContent", + "InternalArticleList", + "LinkedObject", + "LinkedObjectCategory", + "LinkedObjectList", + "LinkedObjectType", + "Metadata", + "MultipleFilterSearchRequest", + "MultipleFilterSearchRequestOperator", + "MultipleFilterSearchRequestValue", + "NewsItemRequest", + "NewsItemRequestState", + "NotFoundErrorBody", + "NotFoundErrorBodyErrorsItem", + "NoteList", + "OffsetPages", + "OpenConversationRequest", + "OperatorWorkflowEvent", + "OperatorWorkflowEventEvent", + "OperatorWorkflowEventWorkflow", + "PagesLink", + "PaginatedResponse", + "PaginatedResponseDataItem", + "PaginatedResponseDataItem_NewsItem", + "PaginatedResponseDataItem_Newsfeed", + "PaginatedResponseType", + "PartAttachment", + "PhoneSwitch", + "QuickReplyOption", + "Recipient", + "RecipientType", + "RedactConversationRequest", + "RedactConversationRequestConversationPart", + "RedactConversationRequestSource", + "RedactConversationRequest_ConversationPart", + "RedactConversationRequest_Source", + "Reference", + "ReplyConversationRequest", + "SearchRequest", + "SearchRequestQuery", + "SegmentList", + "SingleFilterSearchRequest", + "SingleFilterSearchRequestOperator", + "SingleFilterSearchRequestValue", + "SingleFilterSearchRequestValueTwoItem", + "SlaApplied", + "SlaAppliedSlaStatus", + "SnoozeConversationRequest", + "SocialProfile", + "StartingAfterPaging", + "SubscriptionTypeList", + "TagCompanyRequest", + "TagCompanyRequestCompaniesItem", + "TagList", + "TagMultipleUsersRequest", + "TagMultipleUsersRequestUsersItem", + "Tags", + "TeamList", + "TeamPriorityLevel", + "TicketCustomAttributes", + "TicketList", + "TicketPartAuthor", + "TicketPartAuthorType", + "TicketParts", + "TicketReply", + "TicketReplyPartType", + "TicketRequestCustomAttributes", + "TicketStateList", + "TicketTypeAttribute", + "TicketTypeAttributeDataType", + "TicketTypeAttributeList", + "TicketTypeList", + "Translation", + "UntagCompanyRequest", + "UntagCompanyRequestCompaniesItem", + "UpdateArticleRequestBody", + "UpdateArticleRequestBodyParentType", + "UpdateCompanyRequestBody", + "UpdateDataAttributeRequestBody", + "UpdateDataAttributeRequestOptions", + "UpdateDataAttributeRequestOptionsOptionsItem", + "UpdateVisitorRequest", + "UpdateVisitorRequestOne", + "UpdateVisitorRequestWithId", + "UpdateVisitorRequestWithUserId", + "Visitor", + "VisitorAvatar", + "VisitorCompanies", + "VisitorDeletedObject", + "VisitorLocationData", + "VisitorSegments", + "VisitorSocialProfiles", + "VisitorTags", + "VisitorTagsTagsItem", + "WhatsappMessageStatusList", + "WhatsappMessageStatusListEventsItem", + "WhatsappMessageStatusListEventsItemStatus", + "WhatsappMessageStatusListPages", + "WhatsappMessageStatusListPagesNext", +] diff --git a/src/intercom/types/activity_log.py b/src/intercom/types/activity_log.py new file mode 100644 index 00000000..b7759411 --- /dev/null +++ b/src/intercom/types/activity_log.py @@ -0,0 +1,47 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .activity_log_activity_type import ActivityLogActivityType +from .activity_log_metadata import ActivityLogMetadata +from .activity_log_performed_by import ActivityLogPerformedBy + + +class ActivityLog(UncheckedBaseModel): + """ + Activities performed by Admins. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the activity. + """ + + performed_by: typing.Optional[ActivityLogPerformedBy] = pydantic.Field(default=None) + """ + Details about the Admin involved in the activity. + """ + + metadata: typing.Optional[ActivityLogMetadata] = None + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the activity was created. + """ + + activity_type: typing.Optional[ActivityLogActivityType] = None + activity_description: typing.Optional[str] = pydantic.Field(default=None) + """ + A sentence or two describing the activity. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/activity_log_activity_type.py b/src/intercom/types/activity_log_activity_type.py new file mode 100644 index 00000000..8135b337 --- /dev/null +++ b/src/intercom/types/activity_log_activity_type.py @@ -0,0 +1,102 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ActivityLogActivityType = typing.Union[ + typing.Literal[ + "admin_conversation_assignment_limit_change", + "admin_ticket_assignment_limit_change", + "admin_away_mode_change", + "admin_deletion", + "admin_deprovisioned", + "admin_impersonation_end", + "admin_impersonation_start", + "admin_impersonation_consent_approved", + "admin_impersonation_consent_revoked", + "admin_invite_change", + "admin_invite_creation", + "admin_invite_deletion", + "admin_login_failure", + "admin_login_success", + "admin_logout", + "admin_password_reset_request", + "admin_password_reset_success", + "admin_permission_change", + "admin_provisioned", + "admin_two_factor_auth_change", + "admin_unauthorized_sign_in_method", + "app_admin_join", + "app_authentication_method_change", + "app_data_deletion", + "app_data_export", + "app_google_sso_domain_change", + "app_identity_verification_change", + "app_name_change", + "app_outbound_address_change", + "app_package_installation", + "app_package_token_regeneration", + "app_package_uninstallation", + "app_team_creation", + "app_team_deletion", + "app_team_membership_modification", + "app_timezone_change", + "app_webhook_creation", + "app_webhook_deletion", + "articles_in_messenger_enabled_change", + "automatic_away_mode_setting_change", + "bulk_delete", + "bulk_export", + "campaign_deletion", + "campaign_state_change", + "conversation_part_deletion", + "conversation_topic_change", + "conversation_topic_creation", + "conversation_topic_deletion", + "custom_authentication_token_creation", + "help_center_settings_change", + "inbound_conversations_change", + "inbox_access_change", + "macro_creation", + "macro_deletion", + "macro_updated", + "malicious_domains_setting_change", + "message_deletion", + "message_state_change", + "messenger_api_secret_creation", + "messenger_api_secret_deletion", + "messenger_look_and_feel_change", + "messenger_search_required_change", + "messenger_spaces_change", + "oauth_token_revocation", + "office_hours_change", + "role_change", + "role_creation", + "role_deletion", + "ruleset_activation_title_preview", + "ruleset_creation", + "ruleset_deletion", + "search_browse_enabled_change", + "search_browse_required_change", + "seat_change", + "seat_revoke", + "security_settings_change", + "strip_inbound_email_links_change", + "temporary_expectation_change", + "team_assignment_limit_change", + "trusted_domains_setting_change", + "unassign_unsnoozed_at_capacity_setting_change", + "upfront_email_collection_change", + "allowed_attachment_filetypes_setting_change", + "attach_uploads_inline_setting_change", + "teammate_gifs_setting_change", + "user_camera_attachments_setting_change", + "user_conversation_attachments_setting_change", + "user_file_attachments_setting_change", + "user_gifs_setting_change", + "user_media_attachments_setting_change", + "user_voice_notes_setting_change", + "welcome_message_change", + "workspace_deletion_request", + ], + typing.Any, +] diff --git a/src/intercom/types/activity_log_list.py b/src/intercom/types/activity_log_list.py new file mode 100644 index 00000000..3d0bf0aa --- /dev/null +++ b/src/intercom/types/activity_log_list.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .activity_log import ActivityLog +from .cursor_pages import CursorPages + + +class ActivityLogList(UncheckedBaseModel): + """ + A paginated list of activity logs. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `activity_log.list`. + """ + + pages: typing.Optional[CursorPages] = None + activity_logs: typing.Optional[typing.List[typing.Optional[ActivityLog]]] = pydantic.Field(default=None) + """ + An array of activity logs + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/activity_log_metadata.py b/src/intercom/types/activity_log_metadata.py new file mode 100644 index 00000000..1451cad6 --- /dev/null +++ b/src/intercom/types/activity_log_metadata.py @@ -0,0 +1,114 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .activity_log_metadata_team import ActivityLogMetadataTeam + + +class ActivityLogMetadata(UncheckedBaseModel): + """ + Additional data provided about Admin activity. + """ + + sign_in_method: typing.Optional[str] = pydantic.Field(default=None) + """ + The way the admin signed in. + """ + + external_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the contact which is provided by the Client. + """ + + away_mode: typing.Optional[bool] = pydantic.Field(default=None) + """ + The away mode status which is set to true when away and false when returned. + """ + + away_status_reason: typing.Optional[str] = pydantic.Field(default=None) + """ + The reason the Admin is away. + """ + + reassign_conversations: typing.Optional[bool] = pydantic.Field(default=None) + """ + Indicates if conversations should be reassigned while an Admin is away. + """ + + source: typing.Optional[str] = pydantic.Field(default=None) + """ + The action that initiated the status change. + """ + + auto_changed: typing.Optional[str] = pydantic.Field(default=None) + """ + Indicates if the status was changed automatically or manually. + """ + + update_by: typing.Optional[int] = pydantic.Field(default=None) + """ + The ID of the Admin who initiated the activity. + """ + + update_by_name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the Admin who initiated the activity. + """ + + conversation_assignment_limit: typing.Optional[int] = pydantic.Field(default=None) + """ + The conversation assignment limit value for an admin. + """ + + ticket_assignment_limit: typing.Optional[int] = pydantic.Field(default=None) + """ + The ticket assignment limit value for an admin. + """ + + team: typing.Optional[ActivityLogMetadataTeam] = pydantic.Field(default=None) + """ + Details about the team whose assignment limit was changed. + """ + + team_assignment_limit: typing.Optional[int] = pydantic.Field(default=None) + """ + The team assignment limit value (null if limit was removed). + """ + + enabled: typing.Optional[bool] = pydantic.Field(default=None) + """ + Indicates if the setting is enabled or disabled. + """ + + consent_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The ID of the impersonation consent. + """ + + expired_at: typing.Optional[dt.datetime] = pydantic.Field(default=None) + """ + The timestamp when the impersonation consent expires. + """ + + before: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + The state of settings or values before the change. Structure varies by activity type. + """ + + after: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + The state of settings or values after the change. Structure varies by activity type. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/activity_log_metadata_team.py b/src/intercom/types/activity_log_metadata_team.py new file mode 100644 index 00000000..61d1521a --- /dev/null +++ b/src/intercom/types/activity_log_metadata_team.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ActivityLogMetadataTeam(UncheckedBaseModel): + """ + Details about the team whose assignment limit was changed. + """ + + id: typing.Optional[int] = pydantic.Field(default=None) + """ + The ID of the team. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the team. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/activity_log_performed_by.py b/src/intercom/types/activity_log_performed_by.py new file mode 100644 index 00000000..21311c51 --- /dev/null +++ b/src/intercom/types/activity_log_performed_by.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ActivityLogPerformedBy(UncheckedBaseModel): + """ + Details about the Admin involved in the activity. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `admin`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the admin. + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + The email of the admin. + """ + + ip: typing.Optional[str] = pydantic.Field(default=None) + """ + The IP address of the admin. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/addressable_list.py b/src/intercom/types/addressable_list.py new file mode 100644 index 00000000..7c9b76e1 --- /dev/null +++ b/src/intercom/types/addressable_list.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class AddressableList(UncheckedBaseModel): + """ + A list used to access other resources from a parent model. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + The addressable object type + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the addressable object + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + Url to get more company resources for this contact + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/admin_list.py b/src/intercom/types/admin_list.py new file mode 100644 index 00000000..39feacad --- /dev/null +++ b/src/intercom/types/admin_list.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..admins.types.admin import Admin +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class AdminList(UncheckedBaseModel): + """ + A list of admins associated with a given workspace. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `admin.list`. + """ + + admins: typing.Optional[typing.List[typing.Optional[Admin]]] = pydantic.Field(default=None) + """ + A list of admins associated with a given workspace. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/admin_priority_level.py b/src/intercom/types/admin_priority_level.py new file mode 100644 index 00000000..bf6b5d3e --- /dev/null +++ b/src/intercom/types/admin_priority_level.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class AdminPriorityLevel(UncheckedBaseModel): + """ + Admin priority levels for the team + """ + + primary_admin_ids: typing.Optional[typing.List[int]] = pydantic.Field(default=None) + """ + The primary admin ids for the team + """ + + secondary_admin_ids: typing.Optional[typing.List[int]] = pydantic.Field(default=None) + """ + The secondary admin ids for the team + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/admin_reply_conversation_request.py b/src/intercom/types/admin_reply_conversation_request.py new file mode 100644 index 00000000..253ccb32 --- /dev/null +++ b/src/intercom/types/admin_reply_conversation_request.py @@ -0,0 +1,57 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .admin_reply_conversation_request_message_type import AdminReplyConversationRequestMessageType +from .conversation_attachment_files import ConversationAttachmentFiles +from .quick_reply_option import QuickReplyOption + + +class AdminReplyConversationRequest(UncheckedBaseModel): + """ + Payload of the request to reply on behalf of an admin + """ + + message_type: AdminReplyConversationRequestMessageType + type: typing.Literal["admin"] = "admin" + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The text body of the reply. Notes accept some HTML formatting. Must be present for comment and note message types. + """ + + admin_id: str = pydantic.Field() + """ + The id of the admin who is authoring the comment. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the reply was created. If not provided, the current time will be used. + """ + + reply_options: typing.Optional[typing.List[QuickReplyOption]] = pydantic.Field(default=None) + """ + The quick reply options to display to the end user. Must be present for quick_reply message types. + """ + + attachment_urls: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + A list of image URLs that will be added as attachments. You can include up to 10 URLs. + """ + + attachment_files: typing.Optional[typing.List[ConversationAttachmentFiles]] = pydantic.Field(default=None) + """ + A list of files that will be added as attachments. You can include up to 10 files + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/admin_reply_conversation_request_message_type.py b/src/intercom/types/admin_reply_conversation_request_message_type.py new file mode 100644 index 00000000..23e821b7 --- /dev/null +++ b/src/intercom/types/admin_reply_conversation_request_message_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AdminReplyConversationRequestMessageType = typing.Union[typing.Literal["comment", "note", "quick_reply"], typing.Any] diff --git a/src/intercom/types/admin_reply_ticket_request.py b/src/intercom/types/admin_reply_ticket_request.py new file mode 100644 index 00000000..a94ac7a9 --- /dev/null +++ b/src/intercom/types/admin_reply_ticket_request.py @@ -0,0 +1,51 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .admin_reply_ticket_request_message_type import AdminReplyTicketRequestMessageType +from .admin_reply_ticket_request_reply_options_item import AdminReplyTicketRequestReplyOptionsItem + + +class AdminReplyTicketRequest(UncheckedBaseModel): + """ + Payload of the request to reply on behalf of an admin + """ + + message_type: AdminReplyTicketRequestMessageType + type: typing.Literal["admin"] = "admin" + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The text body of the reply. Notes accept some HTML formatting. Must be present for comment and note message types. + """ + + admin_id: str = pydantic.Field() + """ + The id of the admin who is authoring the comment. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the reply was created. If not provided, the current time will be used. + """ + + reply_options: typing.Optional[typing.List[AdminReplyTicketRequestReplyOptionsItem]] = pydantic.Field(default=None) + """ + The quick reply options to display. Must be present for quick_reply message types. + """ + + attachment_urls: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + A list of image URLs that will be added as attachments. You can include up to 10 URLs. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/admin_reply_ticket_request_message_type.py b/src/intercom/types/admin_reply_ticket_request_message_type.py new file mode 100644 index 00000000..1fdcf0c0 --- /dev/null +++ b/src/intercom/types/admin_reply_ticket_request_message_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AdminReplyTicketRequestMessageType = typing.Union[typing.Literal["comment", "note", "quick_reply"], typing.Any] diff --git a/src/intercom/types/admin_reply_ticket_request_reply_options_item.py b/src/intercom/types/admin_reply_ticket_request_reply_options_item.py new file mode 100644 index 00000000..04b828a0 --- /dev/null +++ b/src/intercom/types/admin_reply_ticket_request_reply_options_item.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.serialization import FieldMetadata +from ..core.unchecked_base_model import UncheckedBaseModel + + +class AdminReplyTicketRequestReplyOptionsItem(UncheckedBaseModel): + text: str = pydantic.Field() + """ + The text to display in this quick reply option. + """ + + uuid_: typing_extensions.Annotated[str, FieldMetadata(alias="uuid")] = pydantic.Field() + """ + A unique identifier for this quick reply option. This value will be available within the metadata of the comment ticket part that is created when a user clicks on this reply option. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/admin_with_app.py b/src/intercom/types/admin_with_app.py new file mode 100644 index 00000000..9d8cfa5c --- /dev/null +++ b/src/intercom/types/admin_with_app.py @@ -0,0 +1,84 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .admin_with_app_avatar import AdminWithAppAvatar +from .app import App + + +class AdminWithApp(UncheckedBaseModel): + """ + Admins are the teammate accounts that have access to a workspace + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `admin`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the admin. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the admin. + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + The email of the admin. + """ + + job_title: typing.Optional[str] = pydantic.Field(default=None) + """ + The job title of the admin. + """ + + away_mode_enabled: typing.Optional[bool] = pydantic.Field(default=None) + """ + Identifies if this admin is currently set in away mode. + """ + + away_mode_reassign: typing.Optional[bool] = pydantic.Field(default=None) + """ + Identifies if this admin is set to automatically reassign new conversations to the apps default inbox. + """ + + has_inbox_seat: typing.Optional[bool] = pydantic.Field(default=None) + """ + Identifies if this admin has a paid inbox seat to restrict/allow features that require them. + """ + + team_ids: typing.Optional[typing.List[int]] = pydantic.Field(default=None) + """ + This is a list of ids of the teams that this admin is part of. + """ + + avatar: typing.Optional[AdminWithAppAvatar] = pydantic.Field(default=None) + """ + This object represents the avatar associated with the admin. + """ + + email_verified: typing.Optional[bool] = pydantic.Field(default=None) + """ + Identifies if this admin's email is verified. + """ + + app: typing.Optional[App] = pydantic.Field(default=None) + """ + App that the admin belongs to. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/admin_with_app_avatar.py b/src/intercom/types/admin_with_app_avatar.py new file mode 100644 index 00000000..3495059a --- /dev/null +++ b/src/intercom/types/admin_with_app_avatar.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class AdminWithAppAvatar(UncheckedBaseModel): + """ + This object represents the avatar associated with the admin. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + This is a string that identifies the type of the object. It will always have the value `avatar`. + """ + + image_url: typing.Optional[str] = pydantic.Field(default=None) + """ + This object represents the avatar associated with the admin. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/app.py b/src/intercom/types/app.py new file mode 100644 index 00000000..fed90a51 --- /dev/null +++ b/src/intercom/types/app.py @@ -0,0 +1,57 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class App(UncheckedBaseModel): + """ + App is a workspace on Intercom + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + + """ + + id_code: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the app. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the app. + """ + + region: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom region the app is located in. + """ + + timezone: typing.Optional[str] = pydantic.Field(default=None) + """ + The timezone of the region where the app is located. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + When the app was created. + """ + + identity_verification: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether or not the app uses identity verification. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/article_content.py b/src/intercom/types/article_content.py new file mode 100644 index 00000000..521e9d6e --- /dev/null +++ b/src/intercom/types/article_content.py @@ -0,0 +1,68 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .article_content_state import ArticleContentState + + +class ArticleContent(UncheckedBaseModel): + """ + The Content of an Article. + """ + + type: typing.Optional[typing.Literal["article_content"]] = pydantic.Field(default=None) + """ + The type of object - `article_content` . + """ + + title: typing.Optional[str] = pydantic.Field(default=None) + """ + The title of the article. + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + The description of the article. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The body of the article. + """ + + author_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The ID of the author of the article. + """ + + state: typing.Optional[ArticleContentState] = pydantic.Field(default=None) + """ + Whether the article is `published` or is a `draft` . + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the article was created (seconds). + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the article was last updated (seconds). + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL of the article. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/article_content_state.py b/src/intercom/types/article_content_state.py new file mode 100644 index 00000000..8fbede35 --- /dev/null +++ b/src/intercom/types/article_content_state.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ArticleContentState = typing.Union[typing.Literal["published", "draft"], typing.Any] diff --git a/src/intercom/types/article_list.py b/src/intercom/types/article_list.py new file mode 100644 index 00000000..396e4a68 --- /dev/null +++ b/src/intercom/types/article_list.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..articles.types.article_list_item import ArticleListItem +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ArticleList(UncheckedBaseModel): + """ + This will return a list of articles for the App. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object - `list`. + """ + + pages: typing.Optional[typing.Any] = None + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + A count of the total number of articles. + """ + + data: typing.Optional[typing.List[ArticleListItem]] = pydantic.Field(default=None) + """ + An array of Article objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/article_statistics.py b/src/intercom/types/article_statistics.py new file mode 100644 index 00000000..42009ab8 --- /dev/null +++ b/src/intercom/types/article_statistics.py @@ -0,0 +1,57 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ArticleStatistics(UncheckedBaseModel): + """ + The statistics of an article. + """ + + type: typing.Literal["article_statistics"] = pydantic.Field(default="article_statistics") + """ + The type of object - `article_statistics`. + """ + + views: int = pydantic.Field() + """ + The number of total views the article has received. + """ + + conversions: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of conversations started from the article. + """ + + reactions: int = pydantic.Field() + """ + The number of total reactions the article has received. + """ + + happy_reaction_percentage: float = pydantic.Field() + """ + The percentage of happy reactions the article has received against other types of reaction. + """ + + neutral_reaction_percentage: float = pydantic.Field() + """ + The percentage of neutral reactions the article has received against other types of reaction. + """ + + sad_reaction_percentage: float = pydantic.Field() + """ + The percentage of sad reactions the article has received against other types of reaction. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/article_translated_content.py b/src/intercom/types/article_translated_content.py new file mode 100644 index 00000000..acf59680 --- /dev/null +++ b/src/intercom/types/article_translated_content.py @@ -0,0 +1,221 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.serialization import FieldMetadata +from ..core.unchecked_base_model import UncheckedBaseModel +from .article_content import ArticleContent + + +class ArticleTranslatedContent(UncheckedBaseModel): + """ + The Translated Content of an Article. The keys are the locale codes and the values are the translated content of the article. + """ + + type: typing.Optional[typing.Literal["article_translated_content"]] = pydantic.Field(default=None) + """ + The type of object - article_translated_content. + """ + + ar: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Arabic + """ + + bg: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Bulgarian + """ + + bs: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Bosnian + """ + + ca: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Catalan + """ + + cs: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Czech + """ + + da: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Danish + """ + + de: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in German + """ + + el: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Greek + """ + + en: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in English + """ + + es: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Spanish + """ + + et: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Estonian + """ + + fi: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Finnish + """ + + fr: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in French + """ + + he: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Hebrew + """ + + hr: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Croatian + """ + + hu: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Hungarian + """ + + id: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Indonesian + """ + + it: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Italian + """ + + ja: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Japanese + """ + + ko: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Korean + """ + + lt: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Lithuanian + """ + + lv: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Latvian + """ + + mn: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Mongolian + """ + + nb: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Norwegian + """ + + nl: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Dutch + """ + + pl: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Polish + """ + + pt: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Portuguese (Portugal) + """ + + ro: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Romanian + """ + + ru: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Russian + """ + + sl: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Slovenian + """ + + sr: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Serbian + """ + + sv: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Swedish + """ + + tr: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Turkish + """ + + vi: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Vietnamese + """ + + pt_br: typing_extensions.Annotated[typing.Optional[ArticleContent], FieldMetadata(alias="pt-BR")] = pydantic.Field( + default=None + ) + """ + The content of the article in Portuguese (Brazil) + """ + + zh_cn: typing_extensions.Annotated[typing.Optional[ArticleContent], FieldMetadata(alias="zh-CN")] = pydantic.Field( + default=None + ) + """ + The content of the article in Chinese (China) + """ + + zh_tw: typing_extensions.Annotated[typing.Optional[ArticleContent], FieldMetadata(alias="zh-TW")] = pydantic.Field( + default=None + ) + """ + The content of the article in Chinese (Taiwan) + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/assign_conversation_request.py b/src/intercom/types/assign_conversation_request.py new file mode 100644 index 00000000..dad51261 --- /dev/null +++ b/src/intercom/types/assign_conversation_request.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .assign_conversation_request_type import AssignConversationRequestType + + +class AssignConversationRequest(UncheckedBaseModel): + """ + Payload of the request to assign a conversation + """ + + type: AssignConversationRequestType + admin_id: str = pydantic.Field() + """ + The id of the admin who is performing the action. + """ + + assignee_id: str = pydantic.Field() + """ + The `id` of the `admin` or `team` which will be assigned the conversation. A conversation can be assigned both an admin and a team.\\nSet `0` if you want this assign to no admin or team (ie. Unassigned). + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + Optionally you can send a response in the conversation when it is assigned. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/assign_conversation_request_type.py b/src/intercom/types/assign_conversation_request_type.py new file mode 100644 index 00000000..08756b68 --- /dev/null +++ b/src/intercom/types/assign_conversation_request_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AssignConversationRequestType = typing.Union[typing.Literal["admin", "team"], typing.Any] diff --git a/src/intercom/types/away_status_reason.py b/src/intercom/types/away_status_reason.py new file mode 100644 index 00000000..e17b8753 --- /dev/null +++ b/src/intercom/types/away_status_reason.py @@ -0,0 +1,54 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class AwayStatusReason(UncheckedBaseModel): + type: typing.Optional[str] = None + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the away status reason + """ + + label: typing.Optional[str] = pydantic.Field(default=None) + """ + The display text for the away status reason + """ + + emoji: typing.Optional[str] = pydantic.Field(default=None) + """ + The emoji associated with the status reason + """ + + order: typing.Optional[int] = pydantic.Field(default=None) + """ + The display order of the status reason + """ + + deleted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the status reason has been soft deleted + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The Unix timestamp when the status reason was created + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The Unix timestamp when the status reason was last updated + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/call_list.py b/src/intercom/types/call_list.py new file mode 100644 index 00000000..e1cf1092 --- /dev/null +++ b/src/intercom/types/call_list.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..calls.types.call import Call +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .cursor_pages import CursorPages + + +class CallList(UncheckedBaseModel): + """ + A paginated list of calls. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `list`. + """ + + data: typing.Optional[typing.List[Call]] = pydantic.Field(default=None) + """ + A list of calls. + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + Total number of items available. + """ + + pages: typing.Optional[CursorPages] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/close_conversation_request.py b/src/intercom/types/close_conversation_request.py new file mode 100644 index 00000000..07d5c94e --- /dev/null +++ b/src/intercom/types/close_conversation_request.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CloseConversationRequest(UncheckedBaseModel): + """ + Payload of the request to close a conversation + """ + + type: typing.Literal["admin"] = "admin" + admin_id: str = pydantic.Field() + """ + The id of the admin who is performing the action. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + Optionally you can leave a message in the conversation to provide additional context to the user and other teammates. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/collection_list.py b/src/intercom/types/collection_list.py new file mode 100644 index 00000000..2711d185 --- /dev/null +++ b/src/intercom/types/collection_list.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from ..help_center.types.collection import Collection +from .offset_pages import OffsetPages + + +class CollectionList(UncheckedBaseModel): + """ + This will return a list of Collections for the App. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object - `list`. + """ + + pages: typing.Optional[OffsetPages] = None + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + A count of the total number of collections. + """ + + data: typing.Optional[typing.List[Collection]] = pydantic.Field(default=None) + """ + An array of collection objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/company_attached_contacts.py b/src/intercom/types/company_attached_contacts.py new file mode 100644 index 00000000..6b23ee25 --- /dev/null +++ b/src/intercom/types/company_attached_contacts.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..contacts.types.contact import Contact +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .cursor_pages import CursorPages + + +class CompanyAttachedContacts(UncheckedBaseModel): + """ + A list of Contact Objects + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of object - `list` + """ + + data: typing.Optional[typing.List[Contact]] = pydantic.Field(default=None) + """ + An array containing Contact Objects + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + The total number of contacts + """ + + pages: typing.Optional[CursorPages] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/company_attached_segments.py b/src/intercom/types/company_attached_segments.py new file mode 100644 index 00000000..032bebe2 --- /dev/null +++ b/src/intercom/types/company_attached_segments.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from ..segments.types.segment import Segment + + +class CompanyAttachedSegments(UncheckedBaseModel): + """ + A list of Segment Objects + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of object - `list` + """ + + data: typing.Optional[typing.List[Segment]] = pydantic.Field(default=None) + """ + An array containing Segment Objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/company_data.py b/src/intercom/types/company_data.py new file mode 100644 index 00000000..34ead619 --- /dev/null +++ b/src/intercom/types/company_data.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CompanyData(UncheckedBaseModel): + """ + An object containing data about the companies that a contact is associated with. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the company which is given by Intercom. + """ + + type: typing.Optional[typing.Literal["company"]] = pydantic.Field(default=None) + """ + The type of the object. Always company. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The relative URL of the company. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/company_list.py b/src/intercom/types/company_list.py new file mode 100644 index 00000000..b4bc4a12 --- /dev/null +++ b/src/intercom/types/company_list.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..companies.types.company import Company +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .offset_pages import OffsetPages + + +class CompanyList(UncheckedBaseModel): + """ + This will return a list of companies for the App. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of object - `list`. + """ + + pages: typing.Optional[OffsetPages] = None + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + The total number of companies. + """ + + data: typing.Optional[typing.List[Company]] = pydantic.Field(default=None) + """ + An array containing Company Objects. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/company_scroll.py b/src/intercom/types/company_scroll.py new file mode 100644 index 00000000..a164b4d2 --- /dev/null +++ b/src/intercom/types/company_scroll.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..companies.types.company import Company +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .cursor_pages import CursorPages + + +class CompanyScroll(UncheckedBaseModel): + """ + Companies allow you to represent organizations using your product. Each company will have its own description and be associated with contacts. You can fetch, create, update and list companies. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of object - `list` + """ + + data: typing.Optional[typing.List[Company]] = None + pages: typing.Optional[CursorPages] = None + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + The total number of companies + """ + + scroll_param: typing.Optional[str] = pydantic.Field(default=None) + """ + The scroll parameter to use in the next request to fetch the next page of results. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_archived.py b/src/intercom/types/contact_archived.py new file mode 100644 index 00000000..edbc3cc6 --- /dev/null +++ b/src/intercom/types/contact_archived.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact_reference import ContactReference + + +class ContactArchived(ContactReference): + """ + archived contact object + """ + + archived: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the contact is archived or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_attached_companies.py b/src/intercom/types/contact_attached_companies.py new file mode 100644 index 00000000..01b70305 --- /dev/null +++ b/src/intercom/types/contact_attached_companies.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..companies.types.company import Company +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .pages_link import PagesLink + + +class ContactAttachedCompanies(UncheckedBaseModel): + """ + A list of Company Objects + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of object + """ + + companies: typing.Optional[typing.List[Company]] = pydantic.Field(default=None) + """ + An array containing Company Objects + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + The total number of companies associated to this contact + """ + + pages: typing.Optional[PagesLink] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_blocked.py b/src/intercom/types/contact_blocked.py new file mode 100644 index 00000000..caeb5ac4 --- /dev/null +++ b/src/intercom/types/contact_blocked.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact_reference import ContactReference + + +class ContactBlocked(ContactReference): + """ + blocked contact object + """ + + blocked: typing.Optional[bool] = pydantic.Field(default=None) + """ + Always true. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_companies.py b/src/intercom/types/contact_companies.py new file mode 100644 index 00000000..8975f794 --- /dev/null +++ b/src/intercom/types/contact_companies.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .company_data import CompanyData + + +class ContactCompanies(UncheckedBaseModel): + """ + An object with metadata about companies attached to a contact . Up to 10 will be displayed here. Use the url to get more. + """ + + data: typing.Optional[typing.List[CompanyData]] = pydantic.Field(default=None) + """ + An array of company data objects attached to the contact. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + Url to get more company resources for this contact + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + Integer representing the total number of companies attached to this contact + """ + + has_more: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether there's more Addressable Objects to be viewed. If true, use the url to view all + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_deleted.py b/src/intercom/types/contact_deleted.py new file mode 100644 index 00000000..3b4ba104 --- /dev/null +++ b/src/intercom/types/contact_deleted.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact_reference import ContactReference + + +class ContactDeleted(ContactReference): + """ + deleted contact object + """ + + deleted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the contact is deleted or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_list.py b/src/intercom/types/contact_list.py new file mode 100644 index 00000000..9d1914d0 --- /dev/null +++ b/src/intercom/types/contact_list.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..contacts.types.contact import Contact +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .cursor_pages import CursorPages + + +class ContactList(UncheckedBaseModel): + """ + Contacts are your users in Intercom. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + Always list + """ + + data: typing.Optional[typing.List[Contact]] = pydantic.Field(default=None) + """ + The list of contact objects + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + A count of the total number of objects. + """ + + pages: typing.Optional[CursorPages] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_location.py b/src/intercom/types/contact_location.py new file mode 100644 index 00000000..f24ea02b --- /dev/null +++ b/src/intercom/types/contact_location.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ContactLocation(UncheckedBaseModel): + """ + An object containing location meta data about a Intercom contact. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + Always location + """ + + country: typing.Optional[str] = pydantic.Field(default=None) + """ + The country that the contact is located in + """ + + region: typing.Optional[str] = pydantic.Field(default=None) + """ + The overal region that the contact is located in + """ + + city: typing.Optional[str] = pydantic.Field(default=None) + """ + The city that the contact is located in + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_notes.py b/src/intercom/types/contact_notes.py new file mode 100644 index 00000000..e81bc8c9 --- /dev/null +++ b/src/intercom/types/contact_notes.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .addressable_list import AddressableList + + +class ContactNotes(UncheckedBaseModel): + """ + An object containing notes meta data about the notes that a contact has. Up to 10 will be displayed here. Use the url to get more. + """ + + data: typing.Optional[typing.List[AddressableList]] = pydantic.Field(default=None) + """ + This object represents the notes attached to a contact. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + Url to get more company resources for this contact + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + Int representing the total number of companyies attached to this contact + """ + + has_more: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether there's more Addressable Objects to be viewed. If true, use the url to view all + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_reference.py b/src/intercom/types/contact_reference.py new file mode 100644 index 00000000..5e07f670 --- /dev/null +++ b/src/intercom/types/contact_reference.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ContactReference(UncheckedBaseModel): + """ + reference to contact object + """ + + type: typing.Optional[typing.Literal["contact"]] = pydantic.Field(default=None) + """ + always contact + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the contact which is given by Intercom. + """ + + external_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the contact which is provided by the Client. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_reply_base_request.py b/src/intercom/types/contact_reply_base_request.py new file mode 100644 index 00000000..41d6be39 --- /dev/null +++ b/src/intercom/types/contact_reply_base_request.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .contact_reply_base_request_reply_options_item import ContactReplyBaseRequestReplyOptionsItem + + +class ContactReplyBaseRequest(UncheckedBaseModel): + message_type: typing.Literal["comment"] = "comment" + type: typing.Literal["user"] = "user" + body: str = pydantic.Field() + """ + The text body of the comment. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the reply was created. If not provided, the current time will be used. + """ + + attachment_urls: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + A list of image URLs that will be added as attachments. You can include up to 10 URLs. + """ + + reply_options: typing.Optional[typing.List[ContactReplyBaseRequestReplyOptionsItem]] = pydantic.Field(default=None) + """ + The quick reply selection the contact wishes to respond with. These map to buttons displayed in the Messenger UI if sent by a bot, or the reply options sent by an Admin via the API. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_reply_base_request_reply_options_item.py b/src/intercom/types/contact_reply_base_request_reply_options_item.py new file mode 100644 index 00000000..99bc835b --- /dev/null +++ b/src/intercom/types/contact_reply_base_request_reply_options_item.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.serialization import FieldMetadata +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ContactReplyBaseRequestReplyOptionsItem(UncheckedBaseModel): + text: str = pydantic.Field() + """ + The text of the chosen reply option. + """ + + uuid_: typing_extensions.Annotated[str, FieldMetadata(alias="uuid")] = pydantic.Field() + """ + The unique identifier for the quick reply option selected. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_reply_conversation_request.py b/src/intercom/types/contact_reply_conversation_request.py new file mode 100644 index 00000000..7803e414 --- /dev/null +++ b/src/intercom/types/contact_reply_conversation_request.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .contact_reply_email_request import ContactReplyEmailRequest +from .contact_reply_intercom_user_id_request import ContactReplyIntercomUserIdRequest +from .contact_reply_user_id_request import ContactReplyUserIdRequest + +ContactReplyConversationRequest = typing.Union[ + ContactReplyIntercomUserIdRequest, ContactReplyEmailRequest, ContactReplyUserIdRequest +] diff --git a/src/intercom/types/contact_reply_email_request.py b/src/intercom/types/contact_reply_email_request.py new file mode 100644 index 00000000..c3290d4b --- /dev/null +++ b/src/intercom/types/contact_reply_email_request.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact_reply_base_request import ContactReplyBaseRequest +from .conversation_attachment_files import ConversationAttachmentFiles + + +class ContactReplyEmailRequest(ContactReplyBaseRequest): + """ + Payload of the request to reply on behalf of a contact using their `email` + """ + + email: str = pydantic.Field() + """ + The email you have defined for the user. + """ + + attachment_files: typing.Optional[typing.List[ConversationAttachmentFiles]] = pydantic.Field(default=None) + """ + A list of files that will be added as attachments. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_reply_intercom_user_id_request.py b/src/intercom/types/contact_reply_intercom_user_id_request.py new file mode 100644 index 00000000..f9848364 --- /dev/null +++ b/src/intercom/types/contact_reply_intercom_user_id_request.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact_reply_base_request import ContactReplyBaseRequest +from .conversation_attachment_files import ConversationAttachmentFiles + + +class ContactReplyIntercomUserIdRequest(ContactReplyBaseRequest): + """ + Payload of the request to reply on behalf of a contact using their `intercom_user_id` + """ + + intercom_user_id: str = pydantic.Field() + """ + The identifier for the contact as given by Intercom. + """ + + attachment_files: typing.Optional[typing.List[ConversationAttachmentFiles]] = pydantic.Field(default=None) + """ + A list of files that will be added as attachments. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_reply_ticket_email_request.py b/src/intercom/types/contact_reply_ticket_email_request.py new file mode 100644 index 00000000..b67d8b21 --- /dev/null +++ b/src/intercom/types/contact_reply_ticket_email_request.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact_reply_base_request import ContactReplyBaseRequest + + +class ContactReplyTicketEmailRequest(ContactReplyBaseRequest): + """ + Payload of the request to reply on behalf of a contact using their `email` + """ + + email: str = pydantic.Field() + """ + The email you have defined for the user. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_reply_ticket_intercom_user_id_request.py b/src/intercom/types/contact_reply_ticket_intercom_user_id_request.py new file mode 100644 index 00000000..516dc8d9 --- /dev/null +++ b/src/intercom/types/contact_reply_ticket_intercom_user_id_request.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact_reply_base_request import ContactReplyBaseRequest + + +class ContactReplyTicketIntercomUserIdRequest(ContactReplyBaseRequest): + """ + Payload of the request to reply on behalf of a contact using their `intercom_user_id` + """ + + intercom_user_id: str = pydantic.Field() + """ + The identifier for the contact as given by Intercom. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_reply_ticket_request.py b/src/intercom/types/contact_reply_ticket_request.py new file mode 100644 index 00000000..34225954 --- /dev/null +++ b/src/intercom/types/contact_reply_ticket_request.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .contact_reply_ticket_email_request import ContactReplyTicketEmailRequest +from .contact_reply_ticket_intercom_user_id_request import ContactReplyTicketIntercomUserIdRequest +from .contact_reply_ticket_user_id_request import ContactReplyTicketUserIdRequest + +ContactReplyTicketRequest = typing.Union[ + ContactReplyTicketIntercomUserIdRequest, ContactReplyTicketUserIdRequest, ContactReplyTicketEmailRequest +] diff --git a/src/intercom/types/contact_reply_ticket_user_id_request.py b/src/intercom/types/contact_reply_ticket_user_id_request.py new file mode 100644 index 00000000..ffdceb2f --- /dev/null +++ b/src/intercom/types/contact_reply_ticket_user_id_request.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact_reply_base_request import ContactReplyBaseRequest + + +class ContactReplyTicketUserIdRequest(ContactReplyBaseRequest): + """ + Payload of the request to reply on behalf of a contact using their `user_id` + """ + + user_id: str = pydantic.Field() + """ + The external_id you have defined for the contact. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_reply_user_id_request.py b/src/intercom/types/contact_reply_user_id_request.py new file mode 100644 index 00000000..710c1b20 --- /dev/null +++ b/src/intercom/types/contact_reply_user_id_request.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact_reply_base_request import ContactReplyBaseRequest +from .conversation_attachment_files import ConversationAttachmentFiles + + +class ContactReplyUserIdRequest(ContactReplyBaseRequest): + """ + Payload of the request to reply on behalf of a contact using their `user_id` + """ + + user_id: str = pydantic.Field() + """ + The external_id you have defined for the contact. + """ + + attachment_files: typing.Optional[typing.List[ConversationAttachmentFiles]] = pydantic.Field(default=None) + """ + A list of files that will be added as attachments. You can include up to 10 files. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_segments.py b/src/intercom/types/contact_segments.py new file mode 100644 index 00000000..9431191b --- /dev/null +++ b/src/intercom/types/contact_segments.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from ..segments.types.segment import Segment + + +class ContactSegments(UncheckedBaseModel): + """ + A list of segments objects attached to a specific contact. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + data: typing.Optional[typing.List[Segment]] = pydantic.Field(default=None) + """ + Segment objects associated with the contact. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_social_profiles.py b/src/intercom/types/contact_social_profiles.py new file mode 100644 index 00000000..c00fb2aa --- /dev/null +++ b/src/intercom/types/contact_social_profiles.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .social_profile import SocialProfile + + +class ContactSocialProfiles(UncheckedBaseModel): + """ + An object containing social profiles that a contact has. + """ + + data: typing.Optional[typing.List[SocialProfile]] = pydantic.Field(default=None) + """ + A list of social profiles objects associated with the contact. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_subscription_types.py b/src/intercom/types/contact_subscription_types.py new file mode 100644 index 00000000..a075e34d --- /dev/null +++ b/src/intercom/types/contact_subscription_types.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .addressable_list import AddressableList + + +class ContactSubscriptionTypes(UncheckedBaseModel): + """ + An object containing Subscription Types meta data about the SubscriptionTypes that a contact has. + """ + + data: typing.Optional[typing.List[AddressableList]] = pydantic.Field(default=None) + """ + This object represents the subscriptions attached to a contact. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + Url to get more subscription type resources for this contact + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + Int representing the total number of subscription types attached to this contact + """ + + has_more: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether there's more Addressable Objects to be viewed. If true, use the url to view all + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_tags.py b/src/intercom/types/contact_tags.py new file mode 100644 index 00000000..5f90ba3d --- /dev/null +++ b/src/intercom/types/contact_tags.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .addressable_list import AddressableList + + +class ContactTags(UncheckedBaseModel): + """ + An object containing tags meta data about the tags that a contact has. Up to 10 will be displayed here. Use the url to get more. + """ + + data: typing.Optional[typing.List[AddressableList]] = pydantic.Field(default=None) + """ + This object represents the tags attached to a contact. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + url to get more tag resources for this contact + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + Int representing the total number of tags attached to this contact + """ + + has_more: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether there's more Addressable Objects to be viewed. If true, use the url to view all + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/contact_unarchived.py b/src/intercom/types/contact_unarchived.py new file mode 100644 index 00000000..b194e9f5 --- /dev/null +++ b/src/intercom/types/contact_unarchived.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact_reference import ContactReference + + +class ContactUnarchived(ContactReference): + """ + unarchived contact object + """ + + archived: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the contact is archived or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/content_sources_list.py b/src/intercom/types/content_sources_list.py new file mode 100644 index 00000000..62f2eb13 --- /dev/null +++ b/src/intercom/types/content_sources_list.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..ai_content_source.types.content_source import ContentSource +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ContentSourcesList(UncheckedBaseModel): + type: typing.Optional[typing.Literal["content_source.list"]] = None + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + The total number of content sources used by AI Agent in the conversation. + """ + + content_sources: typing.Optional[typing.List[ContentSource]] = pydantic.Field(default=None) + """ + The content sources used by AI Agent in the conversation. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_attachment_files.py b/src/intercom/types/conversation_attachment_files.py new file mode 100644 index 00000000..92970bcb --- /dev/null +++ b/src/intercom/types/conversation_attachment_files.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ConversationAttachmentFiles(UncheckedBaseModel): + """ + Properties of the attachment files in a conversation part + """ + + content_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The content type of the file + """ + + data: typing.Optional[str] = pydantic.Field(default=None) + """ + The base64 encoded file data. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the file. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_attribute_updated_by_admin.py b/src/intercom/types/conversation_attribute_updated_by_admin.py new file mode 100644 index 00000000..13873aa6 --- /dev/null +++ b/src/intercom/types/conversation_attribute_updated_by_admin.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .conversation_attribute_updated_by_admin_attribute import ConversationAttributeUpdatedByAdminAttribute +from .conversation_attribute_updated_by_admin_value import ConversationAttributeUpdatedByAdminValue + + +class ConversationAttributeUpdatedByAdmin(UncheckedBaseModel): + """ + Contains details about Custom Data Attributes (CDAs) that were modified by an admin (operator) for conversation part type conversation_attribute_updated_by_admin. + """ + + attribute: typing.Optional[ConversationAttributeUpdatedByAdminAttribute] = None + value: typing.Optional[ConversationAttributeUpdatedByAdminValue] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_attribute_updated_by_admin_attribute.py b/src/intercom/types/conversation_attribute_updated_by_admin_attribute.py new file mode 100644 index 00000000..c0d761f0 --- /dev/null +++ b/src/intercom/types/conversation_attribute_updated_by_admin_attribute.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ConversationAttributeUpdatedByAdminAttribute(UncheckedBaseModel): + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Name of the CDA updated + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_attribute_updated_by_admin_value.py b/src/intercom/types/conversation_attribute_updated_by_admin_value.py new file mode 100644 index 00000000..ee5cd5f8 --- /dev/null +++ b/src/intercom/types/conversation_attribute_updated_by_admin_value.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ConversationAttributeUpdatedByAdminValue(UncheckedBaseModel): + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Value of the CDA updated + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_attribute_updated_by_workflow.py b/src/intercom/types/conversation_attribute_updated_by_workflow.py new file mode 100644 index 00000000..c33ff986 --- /dev/null +++ b/src/intercom/types/conversation_attribute_updated_by_workflow.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .conversation_attribute_updated_by_workflow_attribute import ConversationAttributeUpdatedByWorkflowAttribute +from .conversation_attribute_updated_by_workflow_value import ConversationAttributeUpdatedByWorkflowValue +from .conversation_attribute_updated_by_workflow_workflow import ConversationAttributeUpdatedByWorkflowWorkflow + + +class ConversationAttributeUpdatedByWorkflow(UncheckedBaseModel): + """ + Contains details about the workflow that was triggered and any Custom Data Attributes (CDAs) that were modified during the workflow execution for conversation part type conversation_attribute_updated_by_workflow. + """ + + workflow: typing.Optional[ConversationAttributeUpdatedByWorkflowWorkflow] = None + attribute: typing.Optional[ConversationAttributeUpdatedByWorkflowAttribute] = None + value: typing.Optional[ConversationAttributeUpdatedByWorkflowValue] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_attribute_updated_by_workflow_attribute.py b/src/intercom/types/conversation_attribute_updated_by_workflow_attribute.py new file mode 100644 index 00000000..86730a0f --- /dev/null +++ b/src/intercom/types/conversation_attribute_updated_by_workflow_attribute.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ConversationAttributeUpdatedByWorkflowAttribute(UncheckedBaseModel): + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Name of the CDA updated + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_attribute_updated_by_workflow_value.py b/src/intercom/types/conversation_attribute_updated_by_workflow_value.py new file mode 100644 index 00000000..461e4ec0 --- /dev/null +++ b/src/intercom/types/conversation_attribute_updated_by_workflow_value.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ConversationAttributeUpdatedByWorkflowValue(UncheckedBaseModel): + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Value of the CDA updated + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_attribute_updated_by_workflow_workflow.py b/src/intercom/types/conversation_attribute_updated_by_workflow_workflow.py new file mode 100644 index 00000000..445eb7fa --- /dev/null +++ b/src/intercom/types/conversation_attribute_updated_by_workflow_workflow.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ConversationAttributeUpdatedByWorkflowWorkflow(UncheckedBaseModel): + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Name of the workflow + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_contacts.py b/src/intercom/types/conversation_contacts.py new file mode 100644 index 00000000..a216f922 --- /dev/null +++ b/src/intercom/types/conversation_contacts.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .contact_reference import ContactReference + + +class ConversationContacts(UncheckedBaseModel): + """ + The list of contacts (users or leads) involved in this conversation. This will only contain one customer unless more were added via the group conversation feature. + """ + + type: typing.Optional[typing.Literal["contact.list"]] = pydantic.Field(default=None) + """ + + """ + + contacts: typing.Optional[typing.List[ContactReference]] = pydantic.Field(default=None) + """ + The list of contacts (users or leads) involved in this conversation. This will only contain one customer unless more were added via the group conversation feature. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_deleted.py b/src/intercom/types/conversation_deleted.py new file mode 100644 index 00000000..07b8a1c8 --- /dev/null +++ b/src/intercom/types/conversation_deleted.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ConversationDeleted(UncheckedBaseModel): + """ + deleted conversation object + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the conversation. + """ + + object: typing.Optional[typing.Literal["conversation"]] = pydantic.Field(default=None) + """ + always conversation + """ + + deleted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the conversation is deleted or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_first_contact_reply.py b/src/intercom/types/conversation_first_contact_reply.py new file mode 100644 index 00000000..f1580818 --- /dev/null +++ b/src/intercom/types/conversation_first_contact_reply.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ConversationFirstContactReply(UncheckedBaseModel): + """ + An object containing information on the first users message. For a contact initiated message this will represent the users original message. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_list.py b/src/intercom/types/conversation_list.py new file mode 100644 index 00000000..5fad7dfb --- /dev/null +++ b/src/intercom/types/conversation_list.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..conversations.types.conversation import Conversation +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .cursor_pages import CursorPages + + +class ConversationList(UncheckedBaseModel): + """ + Conversations are how you can communicate with users in Intercom. They are created when a contact replies to an outbound message, or when one admin directly sends a message to a single contact. + """ + + type: typing.Optional[typing.Literal["conversation.list"]] = pydantic.Field(default=None) + """ + Always conversation.list + """ + + conversations: typing.Optional[typing.List[Conversation]] = pydantic.Field(default=None) + """ + The list of conversation objects + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + A count of the total number of objects. + """ + + pages: typing.Optional[CursorPages] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_part.py b/src/intercom/types/conversation_part.py new file mode 100644 index 00000000..2766f483 --- /dev/null +++ b/src/intercom/types/conversation_part.py @@ -0,0 +1,104 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from ..tags.types.tag_basic import TagBasic +from .conversation_part_author import ConversationPartAuthor +from .conversation_part_metadata import ConversationPartMetadata +from .conversation_part_state import ConversationPartState +from .email_message_metadata import EmailMessageMetadata +from .event_details import EventDetails +from .part_attachment import PartAttachment +from .reference import Reference + + +class ConversationPart(UncheckedBaseModel): + """ + A Conversation Part represents a message in the conversation. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + Always conversation_part + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the conversation part. + """ + + part_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of conversation part. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The message body, which may contain HTML. For Twitter, this will show a generic message regarding why the body is obscured. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the conversation part was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The last time the conversation part was updated. + """ + + notified_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the user was notified with the conversation part. + """ + + assigned_to: typing.Optional[Reference] = pydantic.Field(default=None) + """ + The id of the admin that was assigned the conversation by this conversation_part (null if there has been no change in assignment.) + """ + + author: typing.Optional[ConversationPartAuthor] = None + attachments: typing.Optional[typing.List[PartAttachment]] = pydantic.Field(default=None) + """ + A list of attachments for the part. + """ + + external_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The external id of the conversation part + """ + + redacted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether or not the conversation part has been redacted. + """ + + email_message_metadata: typing.Optional[EmailMessageMetadata] = None + metadata: typing.Optional[ConversationPartMetadata] = None + state: typing.Optional[ConversationPartState] = pydantic.Field(default=None) + """ + Indicates the current state of conversation when the conversation part was created. + """ + + tags: typing.Optional[typing.List[TagBasic]] = pydantic.Field(default=None) + """ + A list of tags objects associated with the conversation part. + """ + + event_details: typing.Optional[EventDetails] = None + app_package_code: typing.Optional[str] = pydantic.Field(default=None) + """ + The app package code if this part was created via API. null if the part was not created via API. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_part_author.py b/src/intercom/types/conversation_part_author.py new file mode 100644 index 00000000..3af9b148 --- /dev/null +++ b/src/intercom/types/conversation_part_author.py @@ -0,0 +1,52 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ConversationPartAuthor(UncheckedBaseModel): + """ + The object who initiated the conversation, which can be a Contact, Admin or Team. Bots and campaigns send messages on behalf of Admins or Teams. For Twitter, this will be blank. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of the author + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the author + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the author + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + The email of the author + """ + + from_ai_agent: typing.Optional[bool] = pydantic.Field(default=None) + """ + If this conversation part was sent by the AI Agent + """ + + is_ai_answer: typing.Optional[bool] = pydantic.Field(default=None) + """ + If this conversation part body was generated by the AI Agent + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_part_metadata.py b/src/intercom/types/conversation_part_metadata.py new file mode 100644 index 00000000..2b31b86b --- /dev/null +++ b/src/intercom/types/conversation_part_metadata.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .conversation_part_metadata_quick_reply_options_item import ConversationPartMetadataQuickReplyOptionsItem + + +class ConversationPartMetadata(UncheckedBaseModel): + """ + Metadata for a conversation part + """ + + quick_reply_options: typing.Optional[typing.List[ConversationPartMetadataQuickReplyOptionsItem]] = pydantic.Field( + default=None + ) + """ + The quick reply options sent by the Admin or bot, presented in this conversation part. + """ + + quick_reply_uuid: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the quick reply option that was clicked by the end user. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_part_metadata_quick_reply_options_item.py b/src/intercom/types/conversation_part_metadata_quick_reply_options_item.py new file mode 100644 index 00000000..c99f65b5 --- /dev/null +++ b/src/intercom/types/conversation_part_metadata_quick_reply_options_item.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .quick_reply_option import QuickReplyOption + + +class ConversationPartMetadataQuickReplyOptionsItem(QuickReplyOption): + translations: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + The translations for the quick reply option. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_part_state.py b/src/intercom/types/conversation_part_state.py new file mode 100644 index 00000000..96342fb5 --- /dev/null +++ b/src/intercom/types/conversation_part_state.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ConversationPartState = typing.Union[typing.Literal["open", "closed", "snoozed"], typing.Any] diff --git a/src/intercom/types/conversation_parts.py b/src/intercom/types/conversation_parts.py new file mode 100644 index 00000000..e7f31bdd --- /dev/null +++ b/src/intercom/types/conversation_parts.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .conversation_part import ConversationPart + + +class ConversationParts(UncheckedBaseModel): + """ + A list of Conversation Part objects for each part message in the conversation. This is only returned when Retrieving a Conversation, and ignored when Listing all Conversations. There is a limit of 500 parts. + """ + + type: typing.Optional[typing.Literal["conversation_part.list"]] = pydantic.Field(default=None) + """ + + """ + + conversation_parts: typing.Optional[typing.List[ConversationPart]] = pydantic.Field(default=None) + """ + A list of Conversation Part objects for each part message in the conversation. This is only returned when Retrieving a Conversation, and ignored when Listing all Conversations. There is a limit of 500 parts. + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_rating.py b/src/intercom/types/conversation_rating.py new file mode 100644 index 00000000..8f3a6088 --- /dev/null +++ b/src/intercom/types/conversation_rating.py @@ -0,0 +1,47 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .contact_reference import ContactReference +from .reference import Reference + + +class ConversationRating(UncheckedBaseModel): + """ + The Conversation Rating object which contains information on the rating and/or remark added by a Contact and the Admin assigned to the conversation. + """ + + rating: typing.Optional[int] = pydantic.Field(default=None) + """ + The rating, between 1 and 5, for the conversation. + """ + + remark: typing.Optional[str] = pydantic.Field(default=None) + """ + An optional field to add a remark to correspond to the number rating + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the rating was requested in the conversation being rated. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the rating was last updated. + """ + + contact: typing.Optional[ContactReference] = None + teammate: typing.Optional[Reference] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_response_time.py b/src/intercom/types/conversation_response_time.py new file mode 100644 index 00000000..e0e33897 --- /dev/null +++ b/src/intercom/types/conversation_response_time.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ConversationResponseTime(UncheckedBaseModel): + """ + Details of first response time of assigned team in seconds. + """ + + team_id: typing.Optional[int] = pydantic.Field(default=None) + """ + Id of the assigned team. + """ + + team_name: typing.Optional[str] = pydantic.Field(default=None) + """ + Name of the assigned Team, null if team does not exist, Unassigned if no team is assigned. + """ + + response_time: typing.Optional[int] = pydantic.Field(default=None) + """ + First response time of assigned team in seconds. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_source.py b/src/intercom/types/conversation_source.py new file mode 100644 index 00000000..31a365da --- /dev/null +++ b/src/intercom/types/conversation_source.py @@ -0,0 +1,66 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .conversation_part_author import ConversationPartAuthor +from .conversation_source_type import ConversationSourceType +from .part_attachment import PartAttachment + + +class ConversationSource(UncheckedBaseModel): + """ + The type of the conversation part that started this conversation. Can be Contact, Admin, Campaign, Automated or Operator initiated. + """ + + type: typing.Optional[ConversationSourceType] = pydantic.Field(default=None) + """ + This includes conversation, email, facebook, instagram, phone_call, phone_switch, push, sms, twitter and whatsapp. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the message. + """ + + delivered_as: typing.Optional[str] = pydantic.Field(default=None) + """ + The conversation's initiation type. Possible values are customer_initiated, campaigns_initiated (legacy campaigns), operator_initiated (Custom bot), automated (Series and other outbounds with dynamic audience message) and admin_initiated (fixed audience message, ticket initiated by an admin, group email). + """ + + subject: typing.Optional[str] = pydantic.Field(default=None) + """ + Optional. The message subject. For Twitter, this will show a generic message regarding why the subject is obscured. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The message body, which may contain HTML. For Twitter, this will show a generic message regarding why the body is obscured. + """ + + author: typing.Optional[ConversationPartAuthor] = None + attachments: typing.Optional[typing.List[PartAttachment]] = pydantic.Field(default=None) + """ + A list of attachments for the part. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL where the conversation was started. For Twitter, Email, and Bots, this will be blank. + """ + + redacted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether or not the source message has been redacted. Only applicable for contact initiated messages. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_source_type.py b/src/intercom/types/conversation_source_type.py new file mode 100644 index 00000000..0a56fc31 --- /dev/null +++ b/src/intercom/types/conversation_source_type.py @@ -0,0 +1,19 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ConversationSourceType = typing.Union[ + typing.Literal[ + "conversation", + "email", + "facebook", + "instagram", + "phone_call", + "phone_switch", + "push", + "sms", + "twitter", + "whatsapp", + ], + typing.Any, +] diff --git a/src/intercom/types/conversation_statistics.py b/src/intercom/types/conversation_statistics.py new file mode 100644 index 00000000..57c0d9f0 --- /dev/null +++ b/src/intercom/types/conversation_statistics.py @@ -0,0 +1,142 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .conversation_response_time import ConversationResponseTime + + +class ConversationStatistics(UncheckedBaseModel): + """ + A Statistics object containing all information required for reporting, with timestamps and calculated metrics. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + + """ + + time_to_assignment: typing.Optional[int] = pydantic.Field(default=None) + """ + Duration until last assignment before first admin reply. In seconds. + """ + + time_to_admin_reply: typing.Optional[int] = pydantic.Field(default=None) + """ + Duration until first admin reply. Subtracts out of business hours. In seconds. + """ + + time_to_first_close: typing.Optional[int] = pydantic.Field(default=None) + """ + Duration until conversation was closed first time. Subtracts out of business hours. In seconds. + """ + + time_to_last_close: typing.Optional[int] = pydantic.Field(default=None) + """ + Duration until conversation was closed last time. Subtracts out of business hours. In seconds. + """ + + median_time_to_reply: typing.Optional[int] = pydantic.Field(default=None) + """ + Median based on all admin replies after a contact reply. Subtracts out of business hours. In seconds. + """ + + first_contact_reply_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Time of first text conversation part from a contact. + """ + + first_assignment_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Time of first assignment after first_contact_reply_at. + """ + + first_admin_reply_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Time of first admin reply after first_contact_reply_at. + """ + + first_close_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Time of first close after first_contact_reply_at. + """ + + last_assignment_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Time of last assignment after first_contact_reply_at. + """ + + last_assignment_admin_reply_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Time of first admin reply since most recent assignment. + """ + + last_contact_reply_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Time of the last conversation part from a contact. + """ + + last_admin_reply_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Time of the last conversation part from an admin. + """ + + last_close_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Time of the last conversation close. + """ + + last_closed_by_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The last admin who closed the conversation. Returns a reference to an Admin object. + """ + + count_reopens: typing.Optional[int] = pydantic.Field(default=None) + """ + Number of reopens after first_contact_reply_at. + """ + + count_assignments: typing.Optional[int] = pydantic.Field(default=None) + """ + Number of assignments after first_contact_reply_at. + """ + + count_conversation_parts: typing.Optional[int] = pydantic.Field(default=None) + """ + Total number of conversation parts. + """ + + assigned_team_first_response_time_by_team: typing.Optional[typing.List[ConversationResponseTime]] = pydantic.Field( + default=None + ) + """ + An array of conversation response time objects + """ + + assigned_team_first_response_time_in_office_hours: typing.Optional[typing.List[ConversationResponseTime]] = ( + pydantic.Field(default=None) + ) + """ + An array of conversation response time objects within office hours + """ + + handling_time: typing.Optional[int] = pydantic.Field(default=None) + """ + Time from conversation assignment to conversation close in seconds. + """ + + adjusted_handling_time: typing.Optional[int] = pydantic.Field(default=None) + """ + Adjusted handling time for conversation in seconds. This is the active handling time excluding idle periods when teammates are not actively working on the conversation. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/conversation_teammates.py b/src/intercom/types/conversation_teammates.py new file mode 100644 index 00000000..0d9ea9ea --- /dev/null +++ b/src/intercom/types/conversation_teammates.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .reference import Reference + + +class ConversationTeammates(UncheckedBaseModel): + """ + The list of teammates who participated in the conversation (wrote at least one conversation part). + """ + + type: typing.Literal["admin.list"] = pydantic.Field(default="admin.list") + """ + The type of the object - `admin.list`. + """ + + admins: typing.List[Reference] = pydantic.Field() + """ + The list of teammates who participated in the conversation (wrote at least one conversation part). + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_article_request.py b/src/intercom/types/create_article_request.py new file mode 100644 index 00000000..83428ff7 --- /dev/null +++ b/src/intercom/types/create_article_request.py @@ -0,0 +1,62 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .article_translated_content import ArticleTranslatedContent +from .create_article_request_parent_type import CreateArticleRequestParentType +from .create_article_request_state import CreateArticleRequestState + + +class CreateArticleRequest(UncheckedBaseModel): + """ + You can create an Article + """ + + title: str = pydantic.Field() + """ + The title of the article.For multilingual articles, this will be the title of the default language's content. + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + The description of the article. For multilingual articles, this will be the description of the default language's content. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The content of the article. For multilingual articles, this will be the body of the default language's content. + """ + + author_id: int = pydantic.Field() + """ + The id of the author of the article. For multilingual articles, this will be the id of the author of the default language's content. Must be a teammate on the help center's workspace. + """ + + state: typing.Optional[CreateArticleRequestState] = pydantic.Field(default=None) + """ + Whether the article will be `published` or will be a `draft`. Defaults to draft. For multilingual articles, this will be the state of the default language's content. + """ + + parent_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of the article's parent collection or section. An article without this field stands alone. + """ + + parent_type: typing.Optional[CreateArticleRequestParentType] = pydantic.Field(default=None) + """ + The type of parent, which can either be a `collection` or `section`. + """ + + translated_content: typing.Optional[ArticleTranslatedContent] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_article_request_parent_type.py b/src/intercom/types/create_article_request_parent_type.py new file mode 100644 index 00000000..48db0e9a --- /dev/null +++ b/src/intercom/types/create_article_request_parent_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CreateArticleRequestParentType = typing.Union[typing.Literal["collection", "section"], typing.Any] diff --git a/src/intercom/types/create_article_request_state.py b/src/intercom/types/create_article_request_state.py new file mode 100644 index 00000000..2b13f578 --- /dev/null +++ b/src/intercom/types/create_article_request_state.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CreateArticleRequestState = typing.Union[typing.Literal["published", "draft"], typing.Any] diff --git a/src/intercom/types/create_contact_request.py b/src/intercom/types/create_contact_request.py new file mode 100644 index 00000000..054a5eb4 --- /dev/null +++ b/src/intercom/types/create_contact_request.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .create_contact_request_with_email import CreateContactRequestWithEmail +from .create_contact_request_with_external_id import CreateContactRequestWithExternalId +from .create_contact_request_with_role import CreateContactRequestWithRole + +CreateContactRequest = typing.Union[ + CreateContactRequestWithEmail, CreateContactRequestWithExternalId, CreateContactRequestWithRole +] diff --git a/src/intercom/types/create_contact_request_two.py b/src/intercom/types/create_contact_request_two.py new file mode 100644 index 00000000..f5ca73e9 --- /dev/null +++ b/src/intercom/types/create_contact_request_two.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CreateContactRequestTwo = typing.Any diff --git a/src/intercom/types/create_contact_request_with_email.py b/src/intercom/types/create_contact_request_with_email.py new file mode 100644 index 00000000..6ece5e60 --- /dev/null +++ b/src/intercom/types/create_contact_request_with_email.py @@ -0,0 +1,63 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CreateContactRequestWithEmail(UncheckedBaseModel): + email: str = pydantic.Field() + """ + The contacts email + """ + + phone: typing.Optional[str] = pydantic.Field(default=None) + """ + The contacts phone + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The contacts name + """ + + avatar: typing.Optional[str] = pydantic.Field(default=None) + """ + An image URL containing the avatar of a contact + """ + + signed_up_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time specified for when a contact signed up + """ + + last_seen_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the contact was last seen (either where the Intercom Messenger was installed or when specified manually) + """ + + owner_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of an admin that has been assigned account ownership of the contact + """ + + unsubscribed_from_emails: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the contact is unsubscribed from emails + """ + + custom_attributes: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + The custom attributes which are set for the contact + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_contact_request_with_external_id.py b/src/intercom/types/create_contact_request_with_external_id.py new file mode 100644 index 00000000..e9396920 --- /dev/null +++ b/src/intercom/types/create_contact_request_with_external_id.py @@ -0,0 +1,63 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CreateContactRequestWithExternalId(UncheckedBaseModel): + external_id: str = pydantic.Field() + """ + A unique identifier for the contact which is given to Intercom + """ + + phone: typing.Optional[str] = pydantic.Field(default=None) + """ + The contacts phone + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The contacts name + """ + + avatar: typing.Optional[str] = pydantic.Field(default=None) + """ + An image URL containing the avatar of a contact + """ + + signed_up_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time specified for when a contact signed up + """ + + last_seen_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the contact was last seen (either where the Intercom Messenger was installed or when specified manually) + """ + + owner_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of an admin that has been assigned account ownership of the contact + """ + + unsubscribed_from_emails: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the contact is unsubscribed from emails + """ + + custom_attributes: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + The custom attributes which are set for the contact + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_contact_request_with_role.py b/src/intercom/types/create_contact_request_with_role.py new file mode 100644 index 00000000..8a8dba41 --- /dev/null +++ b/src/intercom/types/create_contact_request_with_role.py @@ -0,0 +1,63 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CreateContactRequestWithRole(UncheckedBaseModel): + role: str = pydantic.Field() + """ + The role of the contact. + """ + + phone: typing.Optional[str] = pydantic.Field(default=None) + """ + The contacts phone + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The contacts name + """ + + avatar: typing.Optional[str] = pydantic.Field(default=None) + """ + An image URL containing the avatar of a contact + """ + + signed_up_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time specified for when a contact signed up + """ + + last_seen_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the contact was last seen (either where the Intercom Messenger was installed or when specified manually) + """ + + owner_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of an admin that has been assigned account ownership of the contact + """ + + unsubscribed_from_emails: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the contact is unsubscribed from emails + """ + + custom_attributes: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + The custom attributes which are set for the contact + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_data_attribute_request.py b/src/intercom/types/create_data_attribute_request.py new file mode 100644 index 00000000..cd76eee5 --- /dev/null +++ b/src/intercom/types/create_data_attribute_request.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .create_data_attribute_request_one import CreateDataAttributeRequestOne +from .create_data_attribute_request_options import CreateDataAttributeRequestOptions + +CreateDataAttributeRequest = typing.Union[CreateDataAttributeRequestOptions, CreateDataAttributeRequestOne] diff --git a/src/intercom/types/create_data_attribute_request_one.py b/src/intercom/types/create_data_attribute_request_one.py new file mode 100644 index 00000000..1693ecdf --- /dev/null +++ b/src/intercom/types/create_data_attribute_request_one.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .create_data_attribute_request_one_data_type import CreateDataAttributeRequestOneDataType + + +class CreateDataAttributeRequestOne(UncheckedBaseModel): + data_type: typing.Optional[CreateDataAttributeRequestOneDataType] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_data_attribute_request_one_data_type.py b/src/intercom/types/create_data_attribute_request_one_data_type.py new file mode 100644 index 00000000..0689e73c --- /dev/null +++ b/src/intercom/types/create_data_attribute_request_one_data_type.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CreateDataAttributeRequestOneDataType = typing.Union[ + typing.Literal["string", "integer", "float", "boolean", "datetime", "date"], typing.Any +] diff --git a/src/intercom/types/create_data_attribute_request_options.py b/src/intercom/types/create_data_attribute_request_options.py new file mode 100644 index 00000000..4cfcb975 --- /dev/null +++ b/src/intercom/types/create_data_attribute_request_options.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .create_data_attribute_request_options_options_item import CreateDataAttributeRequestOptionsOptionsItem + + +class CreateDataAttributeRequestOptions(UncheckedBaseModel): + data_type: typing.Optional[typing.Literal["options"]] = None + options: typing.List[CreateDataAttributeRequestOptionsOptionsItem] = pydantic.Field() + """ + Array of objects representing the options of the list, with `value` as the key and the option as the value. At least two options are required. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_data_attribute_request_options_options_item.py b/src/intercom/types/create_data_attribute_request_options_options_item.py new file mode 100644 index 00000000..e879b4fd --- /dev/null +++ b/src/intercom/types/create_data_attribute_request_options_options_item.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CreateDataAttributeRequestOptionsOptionsItem(UncheckedBaseModel): + value: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_data_event_request.py b/src/intercom/types/create_data_event_request.py new file mode 100644 index 00000000..89437be5 --- /dev/null +++ b/src/intercom/types/create_data_event_request.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .create_data_event_request_with_email import CreateDataEventRequestWithEmail +from .create_data_event_request_with_id import CreateDataEventRequestWithId +from .create_data_event_request_with_user_id import CreateDataEventRequestWithUserId + +CreateDataEventRequest = typing.Union[ + CreateDataEventRequestWithId, CreateDataEventRequestWithUserId, CreateDataEventRequestWithEmail +] diff --git a/src/intercom/types/create_data_event_request_two.py b/src/intercom/types/create_data_event_request_two.py new file mode 100644 index 00000000..a705a7cf --- /dev/null +++ b/src/intercom/types/create_data_event_request_two.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CreateDataEventRequestTwo = typing.Any diff --git a/src/intercom/types/create_data_event_request_with_email.py b/src/intercom/types/create_data_event_request_with_email.py new file mode 100644 index 00000000..83dc54be --- /dev/null +++ b/src/intercom/types/create_data_event_request_with_email.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CreateDataEventRequestWithEmail(UncheckedBaseModel): + email: str = pydantic.Field() + """ + An email address for your user. An email should only be used where your application uses email to uniquely identify users. + """ + + event_name: str = pydantic.Field() + """ + The name of the event that occurred. This is presented to your App's admins when filtering and creating segments - a good event name is typically a past tense 'verb-noun' combination, to improve readability, for example `updated-plan`. + """ + + created_at: int = pydantic.Field() + """ + The time the event occurred as a UTC Unix timestamp + """ + + metadata: typing.Optional[typing.Dict[str, str]] = pydantic.Field(default=None) + """ + Optional metadata about the event. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_data_event_request_with_id.py b/src/intercom/types/create_data_event_request_with_id.py new file mode 100644 index 00000000..13f1d109 --- /dev/null +++ b/src/intercom/types/create_data_event_request_with_id.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CreateDataEventRequestWithId(UncheckedBaseModel): + id: str = pydantic.Field() + """ + The unique identifier for the contact (lead or user) which is given by Intercom. + """ + + event_name: str = pydantic.Field() + """ + The name of the event that occurred. This is presented to your App's admins when filtering and creating segments - a good event name is typically a past tense 'verb-noun' combination, to improve readability, for example `updated-plan`. + """ + + created_at: int = pydantic.Field() + """ + The time the event occurred as a UTC Unix timestamp + """ + + metadata: typing.Optional[typing.Dict[str, str]] = pydantic.Field(default=None) + """ + Optional metadata about the event. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_data_event_request_with_user_id.py b/src/intercom/types/create_data_event_request_with_user_id.py new file mode 100644 index 00000000..ad494975 --- /dev/null +++ b/src/intercom/types/create_data_event_request_with_user_id.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CreateDataEventRequestWithUserId(UncheckedBaseModel): + user_id: str = pydantic.Field() + """ + Your identifier for the user. + """ + + event_name: str = pydantic.Field() + """ + The name of the event that occurred. This is presented to your App's admins when filtering and creating segments - a good event name is typically a past tense 'verb-noun' combination, to improve readability, for example `updated-plan`. + """ + + created_at: int = pydantic.Field() + """ + The time the event occurred as a UTC Unix timestamp + """ + + metadata: typing.Optional[typing.Dict[str, str]] = pydantic.Field(default=None) + """ + Optional metadata about the event. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_internal_article_request.py b/src/intercom/types/create_internal_article_request.py new file mode 100644 index 00000000..8a269879 --- /dev/null +++ b/src/intercom/types/create_internal_article_request.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CreateInternalArticleRequest(UncheckedBaseModel): + """ + You can create an Internal Article + """ + + title: str = pydantic.Field() + """ + The title of the article. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The content of the article. + """ + + author_id: int = pydantic.Field() + """ + The id of the author of the article. + """ + + owner_id: int = pydantic.Field() + """ + The id of the owner of the article. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_message_request.py b/src/intercom/types/create_message_request.py new file mode 100644 index 00000000..d10e75fc --- /dev/null +++ b/src/intercom/types/create_message_request.py @@ -0,0 +1,66 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import pydantic +import typing_extensions +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.serialization import FieldMetadata +from ..core.unchecked_base_model import UncheckedBaseModel, UnionMetadata +from .create_message_request_from import CreateMessageRequestFrom +from .create_message_request_to import CreateMessageRequestTo + + +class CreateMessageRequest_Email(UncheckedBaseModel): + """ + You can create a message + """ + + message_type: typing.Literal["email"] = "email" + subject: str + body: str + template: str + from_: typing_extensions.Annotated[CreateMessageRequestFrom, FieldMetadata(alias="from")] + to: CreateMessageRequestTo + created_at: typing.Optional[int] = None + create_conversation_without_contact_reply: typing.Optional[bool] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class CreateMessageRequest_Inapp(UncheckedBaseModel): + """ + You can create a message + """ + + message_type: typing.Literal["inapp"] = "inapp" + subject: typing.Optional[str] = None + body: str + template: typing.Optional[str] = None + from_: typing_extensions.Annotated[CreateMessageRequestFrom, FieldMetadata(alias="from")] + to: CreateMessageRequestTo + created_at: typing.Optional[int] = None + create_conversation_without_contact_reply: typing.Optional[bool] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +CreateMessageRequest = typing_extensions.Annotated[ + typing.Union[CreateMessageRequest_Email, CreateMessageRequest_Inapp], UnionMetadata(discriminant="message_type") +] diff --git a/src/intercom/types/create_message_request_from.py b/src/intercom/types/create_message_request_from.py new file mode 100644 index 00000000..784fd7a2 --- /dev/null +++ b/src/intercom/types/create_message_request_from.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CreateMessageRequestFrom(UncheckedBaseModel): + """ + The sender of the message. If not provided, the default sender will be used. + """ + + type: typing.Literal["admin"] = pydantic.Field(default="admin") + """ + Always `admin`. + """ + + id: int = pydantic.Field() + """ + The identifier for the admin which is given by Intercom. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_message_request_to.py b/src/intercom/types/create_message_request_to.py new file mode 100644 index 00000000..6e462f89 --- /dev/null +++ b/src/intercom/types/create_message_request_to.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .create_message_request_type import CreateMessageRequestType + + +class CreateMessageRequestTo(UncheckedBaseModel): + """ + The sender of the message. If not provided, the default sender will be used. + """ + + type: CreateMessageRequestType = pydantic.Field() + """ + The role associated to the contact - `user` or `lead`. + """ + + id: str = pydantic.Field() + """ + The identifier for the contact which is given by Intercom. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_message_request_type.py b/src/intercom/types/create_message_request_type.py new file mode 100644 index 00000000..0d27b8eb --- /dev/null +++ b/src/intercom/types/create_message_request_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CreateMessageRequestType = typing.Union[typing.Literal["user", "lead"], typing.Any] diff --git a/src/intercom/types/create_message_request_with_email.py b/src/intercom/types/create_message_request_with_email.py new file mode 100644 index 00000000..af096193 --- /dev/null +++ b/src/intercom/types/create_message_request_with_email.py @@ -0,0 +1,57 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.serialization import FieldMetadata +from ..core.unchecked_base_model import UncheckedBaseModel +from .create_message_request_from import CreateMessageRequestFrom +from .create_message_request_to import CreateMessageRequestTo + + +class CreateMessageRequestWithEmail(UncheckedBaseModel): + subject: str = pydantic.Field() + """ + The title of the email. + """ + + body: str = pydantic.Field() + """ + The content of the message. HTML and plaintext are supported. + """ + + template: str = pydantic.Field() + """ + The style of the outgoing message. Possible values `plain` or `personal`. + """ + + from_: typing_extensions.Annotated[CreateMessageRequestFrom, FieldMetadata(alias="from")] = pydantic.Field() + """ + The sender of the message. If not provided, the default sender will be used. + """ + + to: CreateMessageRequestTo = pydantic.Field() + """ + The sender of the message. If not provided, the default sender will be used. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the message was created. If not provided, the current time will be used. + """ + + create_conversation_without_contact_reply: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether a conversation should be opened in the inbox for the message without the contact replying. Defaults to false if not provided. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_message_request_with_inapp.py b/src/intercom/types/create_message_request_with_inapp.py new file mode 100644 index 00000000..239667fd --- /dev/null +++ b/src/intercom/types/create_message_request_with_inapp.py @@ -0,0 +1,57 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.serialization import FieldMetadata +from ..core.unchecked_base_model import UncheckedBaseModel +from .create_message_request_from import CreateMessageRequestFrom +from .create_message_request_to import CreateMessageRequestTo + + +class CreateMessageRequestWithInapp(UncheckedBaseModel): + subject: typing.Optional[str] = pydantic.Field(default=None) + """ + The title of the email. + """ + + body: str = pydantic.Field() + """ + The content of the message. HTML and plaintext are supported. + """ + + template: typing.Optional[str] = pydantic.Field(default=None) + """ + The style of the outgoing message. Possible values `plain` or `personal`. + """ + + from_: typing_extensions.Annotated[CreateMessageRequestFrom, FieldMetadata(alias="from")] = pydantic.Field() + """ + The sender of the message. If not provided, the default sender will be used. + """ + + to: CreateMessageRequestTo = pydantic.Field() + """ + The sender of the message. If not provided, the default sender will be used. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the message was created. If not provided, the current time will be used. + """ + + create_conversation_without_contact_reply: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether a conversation should be opened in the inbox for the message without the contact replying. Defaults to false if not provided. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_or_update_company_request.py b/src/intercom/types/create_or_update_company_request.py new file mode 100644 index 00000000..567f8be5 --- /dev/null +++ b/src/intercom/types/create_or_update_company_request.py @@ -0,0 +1,67 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CreateOrUpdateCompanyRequest(UncheckedBaseModel): + """ + You can create or update a Company + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the Company + """ + + company_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The company id you have defined for the company. Can't be updated + """ + + plan: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the plan you have associated with the company. + """ + + size: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of employees in this company. + """ + + website: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL for this company's website. Please note that the value specified here is not validated. Accepts any string. + """ + + industry: typing.Optional[str] = pydantic.Field(default=None) + """ + The industry that this company operates in. + """ + + custom_attributes: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + A hash of key/value pairs containing any other data about the company you want Intercom to store. + """ + + remote_created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the company was created by you. + """ + + monthly_spend: typing.Optional[int] = pydantic.Field(default=None) + """ + How much revenue the company generates for your business. Note that this will truncate floats. i.e. it only allow for whole integers, 155.98 will be truncated to 155. Note that this has an upper limit of 2**31-1 or 2147483647.. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_or_update_tag_request.py b/src/intercom/types/create_or_update_tag_request.py new file mode 100644 index 00000000..31129c9b --- /dev/null +++ b/src/intercom/types/create_or_update_tag_request.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CreateOrUpdateTagRequest(UncheckedBaseModel): + """ + You can create or update an existing tag. + """ + + name: str = pydantic.Field() + """ + The name of the tag, which will be created if not found, or the new name for the tag if this is an update request. Names are case insensitive. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of tag to updates. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_phone_switch_request.py b/src/intercom/types/create_phone_switch_request.py new file mode 100644 index 00000000..988e85d5 --- /dev/null +++ b/src/intercom/types/create_phone_switch_request.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .custom_attributes import CustomAttributes + + +class CreatePhoneSwitchRequest(UncheckedBaseModel): + """ + You can create an phone switch + """ + + phone: str = pydantic.Field() + """ + Phone number in E.164 format, that will receive the SMS to continue the conversation in the Messenger. + """ + + custom_attributes: typing.Optional[CustomAttributes] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_ticket_reply_with_comment_request.py b/src/intercom/types/create_ticket_reply_with_comment_request.py new file mode 100644 index 00000000..107d44a0 --- /dev/null +++ b/src/intercom/types/create_ticket_reply_with_comment_request.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .admin_reply_ticket_request import AdminReplyTicketRequest +from .contact_reply_ticket_request import ContactReplyTicketRequest + +CreateTicketReplyWithCommentRequest = typing.Union[ContactReplyTicketRequest, AdminReplyTicketRequest] diff --git a/src/intercom/types/create_ticket_request_assignment.py b/src/intercom/types/create_ticket_request_assignment.py new file mode 100644 index 00000000..8c706189 --- /dev/null +++ b/src/intercom/types/create_ticket_request_assignment.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CreateTicketRequestAssignment(UncheckedBaseModel): + admin_assignee_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the admin to which the ticket is assigned. If not provided, the ticket will be unassigned. + """ + + team_assignee_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the team to which the ticket is assigned. If not provided, the ticket will be unassigned. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_ticket_request_body.py b/src/intercom/types/create_ticket_request_body.py new file mode 100644 index 00000000..7680a221 --- /dev/null +++ b/src/intercom/types/create_ticket_request_body.py @@ -0,0 +1,55 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .create_ticket_request_assignment import CreateTicketRequestAssignment +from .create_ticket_request_contacts_item import CreateTicketRequestContactsItem + + +class CreateTicketRequestBody(UncheckedBaseModel): + """ + You can create a Ticket + """ + + ticket_type_id: str = pydantic.Field() + """ + The ID of the type of ticket you want to create + """ + + contacts: typing.List[CreateTicketRequestContactsItem] = pydantic.Field() + """ + The list of contacts (users or leads) affected by this ticket. Currently only one is allowed + """ + + conversation_to_link_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the conversation you want to link to the ticket. Here are the valid ways of linking two tickets: + - conversation | back-office ticket + - customer tickets | non-shared back-office ticket + - conversation | tracker ticket + - customer ticket | tracker ticket + """ + + company_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the ticket was created. If not provided, the current time will be used. + """ + + assignment: typing.Optional[CreateTicketRequestAssignment] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_ticket_request_contacts_item.py b/src/intercom/types/create_ticket_request_contacts_item.py new file mode 100644 index 00000000..2d5d66c6 --- /dev/null +++ b/src/intercom/types/create_ticket_request_contacts_item.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .create_ticket_request_contacts_item_email import CreateTicketRequestContactsItemEmail +from .create_ticket_request_contacts_item_external_id import CreateTicketRequestContactsItemExternalId +from .create_ticket_request_contacts_item_id import CreateTicketRequestContactsItemId + +CreateTicketRequestContactsItem = typing.Union[ + CreateTicketRequestContactsItemId, CreateTicketRequestContactsItemExternalId, CreateTicketRequestContactsItemEmail +] diff --git a/src/intercom/types/create_ticket_request_contacts_item_email.py b/src/intercom/types/create_ticket_request_contacts_item_email.py new file mode 100644 index 00000000..45941af8 --- /dev/null +++ b/src/intercom/types/create_ticket_request_contacts_item_email.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CreateTicketRequestContactsItemEmail(UncheckedBaseModel): + email: str = pydantic.Field() + """ + The email you have defined for the contact who is being added as a participant. If a contact with this email does not exist, one will be created. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_ticket_request_contacts_item_external_id.py b/src/intercom/types/create_ticket_request_contacts_item_external_id.py new file mode 100644 index 00000000..96a1cef1 --- /dev/null +++ b/src/intercom/types/create_ticket_request_contacts_item_external_id.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CreateTicketRequestContactsItemExternalId(UncheckedBaseModel): + external_id: str = pydantic.Field() + """ + The external_id you have defined for the contact who is being added as a participant. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_ticket_request_contacts_item_id.py b/src/intercom/types/create_ticket_request_contacts_item_id.py new file mode 100644 index 00000000..26859f13 --- /dev/null +++ b/src/intercom/types/create_ticket_request_contacts_item_id.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CreateTicketRequestContactsItemId(UncheckedBaseModel): + id: str = pydantic.Field() + """ + The identifier for the contact as given by Intercom. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_ticket_type_request.py b/src/intercom/types/create_ticket_type_request.py new file mode 100644 index 00000000..5ce9d1e0 --- /dev/null +++ b/src/intercom/types/create_ticket_type_request.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .create_ticket_type_request_category import CreateTicketTypeRequestCategory + + +class CreateTicketTypeRequest(UncheckedBaseModel): + """ + The request payload for creating a ticket type. + You can copy the `icon` property for your ticket type from [Twemoji Cheatsheet](https://twemoji-cheatsheet.vercel.app/) + """ + + name: str = pydantic.Field() + """ + The name of the ticket type. + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + The description of the ticket type. + """ + + category: typing.Optional[CreateTicketTypeRequestCategory] = pydantic.Field(default=None) + """ + Category of the Ticket Type. + """ + + icon: typing.Optional[str] = pydantic.Field(default=None) + """ + The icon of the ticket type. + """ + + is_internal: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the tickets associated with this ticket type are intended for internal use only or will be shared with customers. This is currently a limited attribute. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/create_ticket_type_request_category.py b/src/intercom/types/create_ticket_type_request_category.py new file mode 100644 index 00000000..be7783af --- /dev/null +++ b/src/intercom/types/create_ticket_type_request_category.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CreateTicketTypeRequestCategory = typing.Union[typing.Literal["Customer", "Back-office", "Tracker"], typing.Any] diff --git a/src/intercom/types/cursor_pages.py b/src/intercom/types/cursor_pages.py new file mode 100644 index 00000000..9877488a --- /dev/null +++ b/src/intercom/types/cursor_pages.py @@ -0,0 +1,45 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .starting_after_paging import StartingAfterPaging + + +class CursorPages(UncheckedBaseModel): + """ + Cursor-based pagination is a technique used in the Intercom API to navigate through large amounts of data. + A "cursor" or pointer is used to keep track of the current position in the result set, allowing the API to return the data in small chunks or "pages" as needed. + """ + + type: typing.Optional[typing.Literal["pages"]] = pydantic.Field(default=None) + """ + the type of object `pages`. + """ + + page: typing.Optional[int] = pydantic.Field(default=None) + """ + The current page + """ + + next: typing.Optional[StartingAfterPaging] = None + per_page: typing.Optional[int] = pydantic.Field(default=None) + """ + Number of results per page + """ + + total_pages: typing.Optional[int] = pydantic.Field(default=None) + """ + Total number of pages + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/custom_action_finished.py b/src/intercom/types/custom_action_finished.py new file mode 100644 index 00000000..4054d045 --- /dev/null +++ b/src/intercom/types/custom_action_finished.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .custom_action_finished_action import CustomActionFinishedAction + + +class CustomActionFinished(UncheckedBaseModel): + """ + Contains details about final status of the completed action for conversation part type custom_action_finished. + """ + + action: typing.Optional[CustomActionFinishedAction] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/custom_action_finished_action.py b/src/intercom/types/custom_action_finished_action.py new file mode 100644 index 00000000..7c05f070 --- /dev/null +++ b/src/intercom/types/custom_action_finished_action.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .custom_action_finished_action_result import CustomActionFinishedActionResult + + +class CustomActionFinishedAction(UncheckedBaseModel): + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Name of the action + """ + + result: typing.Optional[CustomActionFinishedActionResult] = pydantic.Field(default=None) + """ + Status of the action + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/custom_action_finished_action_result.py b/src/intercom/types/custom_action_finished_action_result.py new file mode 100644 index 00000000..0f1e35ab --- /dev/null +++ b/src/intercom/types/custom_action_finished_action_result.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CustomActionFinishedActionResult = typing.Union[typing.Literal["success", "failed"], typing.Any] diff --git a/src/intercom/types/custom_action_started.py b/src/intercom/types/custom_action_started.py new file mode 100644 index 00000000..de8480ed --- /dev/null +++ b/src/intercom/types/custom_action_started.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .custom_action_started_action import CustomActionStartedAction + + +class CustomActionStarted(UncheckedBaseModel): + """ + Contains details about name of the action that was initiated for conversation part type custom_action_started. + """ + + action: typing.Optional[CustomActionStartedAction] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/custom_action_started_action.py b/src/intercom/types/custom_action_started_action.py new file mode 100644 index 00000000..1bc8192f --- /dev/null +++ b/src/intercom/types/custom_action_started_action.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CustomActionStartedAction(UncheckedBaseModel): + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Name of the action + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/custom_attributes.py b/src/intercom/types/custom_attributes.py new file mode 100644 index 00000000..52b1b173 --- /dev/null +++ b/src/intercom/types/custom_attributes.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .custom_attributes_value import CustomAttributesValue + +CustomAttributes = typing.Dict[str, CustomAttributesValue] diff --git a/src/intercom/types/custom_attributes_value.py b/src/intercom/types/custom_attributes_value.py new file mode 100644 index 00000000..93669f70 --- /dev/null +++ b/src/intercom/types/custom_attributes_value.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .custom_object_instance_list import CustomObjectInstanceList +from .datetime import Datetime + +CustomAttributesValue = typing.Union[str, int, Datetime, CustomObjectInstanceList] diff --git a/src/intercom/types/custom_channel_attribute.py b/src/intercom/types/custom_channel_attribute.py new file mode 100644 index 00000000..88d39944 --- /dev/null +++ b/src/intercom/types/custom_channel_attribute.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CustomChannelAttribute(UncheckedBaseModel): + id: str = pydantic.Field() + """ + Identifier for the attribute being collected. + """ + + value: str = pydantic.Field() + """ + Value provided by the user for the attribute. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/custom_channel_base_event.py b/src/intercom/types/custom_channel_base_event.py new file mode 100644 index 00000000..3670a058 --- /dev/null +++ b/src/intercom/types/custom_channel_base_event.py @@ -0,0 +1,31 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .custom_channel_contact import CustomChannelContact + + +class CustomChannelBaseEvent(UncheckedBaseModel): + event_id: str = pydantic.Field() + """ + Unique identifier for the event. + """ + + external_conversation_id: str = pydantic.Field() + """ + Identifier for the conversation in your application. + """ + + contact: CustomChannelContact + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/custom_channel_contact.py b/src/intercom/types/custom_channel_contact.py new file mode 100644 index 00000000..cb1d76be --- /dev/null +++ b/src/intercom/types/custom_channel_contact.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .custom_channel_contact_type import CustomChannelContactType + + +class CustomChannelContact(UncheckedBaseModel): + type: CustomChannelContactType = pydantic.Field() + """ + Type of contact, must be "user" or "lead". + """ + + external_id: str = pydantic.Field() + """ + External identifier for the contact. Intercom will take care of the mapping of your external_id with our internal ones so you don't have to worry about it. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Name of the contact. Required for user type. + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + Email address of the contact. Required for user type. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/custom_channel_contact_type.py b/src/intercom/types/custom_channel_contact_type.py new file mode 100644 index 00000000..de33161a --- /dev/null +++ b/src/intercom/types/custom_channel_contact_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CustomChannelContactType = typing.Union[typing.Literal["user", "lead"], typing.Any] diff --git a/src/intercom/types/custom_channel_notification_response.py b/src/intercom/types/custom_channel_notification_response.py new file mode 100644 index 00000000..394f61ca --- /dev/null +++ b/src/intercom/types/custom_channel_notification_response.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CustomChannelNotificationResponse(UncheckedBaseModel): + external_conversation_id: str = pydantic.Field() + """ + The external conversation ID provided in the notification request + """ + + conversation_id: str = pydantic.Field() + """ + The Intercom conversation ID mapped to the external conversation ID + """ + + external_contact_id: str = pydantic.Field() + """ + The external contact ID provided in the notification request + """ + + contact_id: str = pydantic.Field() + """ + The Intercom contact ID mapped to the external contact ID + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/custom_object_instance_deleted.py b/src/intercom/types/custom_object_instance_deleted.py new file mode 100644 index 00000000..5d401bf5 --- /dev/null +++ b/src/intercom/types/custom_object_instance_deleted.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CustomObjectInstanceDeleted(UncheckedBaseModel): + """ + deleted custom object instance object + """ + + object: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier of the Custom Object type that defines the structure of the Custom Object instance. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom defined id representing the Custom Object instance. + """ + + deleted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the Custom Object instance is deleted or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/custom_object_instance_list.py b/src/intercom/types/custom_object_instance_list.py new file mode 100644 index 00000000..9cc9735a --- /dev/null +++ b/src/intercom/types/custom_object_instance_list.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from ..custom_object_instances.types.custom_object_instance import CustomObjectInstance + + +class CustomObjectInstanceList(UncheckedBaseModel): + """ + The list of associated custom object instances for a given reference attribute on the parent object. + """ + + type: typing.Optional[str] = None + instances: typing.Optional[typing.List[typing.Optional[CustomObjectInstance]]] = pydantic.Field(default=None) + """ + The list of associated custom object instances for a given reference attribute on the parent object. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/customer_request.py b/src/intercom/types/customer_request.py new file mode 100644 index 00000000..a61840b5 --- /dev/null +++ b/src/intercom/types/customer_request.py @@ -0,0 +1,9 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .customer_request_email import CustomerRequestEmail +from .customer_request_intercom_user_id import CustomerRequestIntercomUserId +from .customer_request_user_id import CustomerRequestUserId + +CustomerRequest = typing.Union[CustomerRequestIntercomUserId, CustomerRequestUserId, CustomerRequestEmail] diff --git a/src/intercom/types/customer_request_email.py b/src/intercom/types/customer_request_email.py new file mode 100644 index 00000000..2291e9b1 --- /dev/null +++ b/src/intercom/types/customer_request_email.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CustomerRequestEmail(UncheckedBaseModel): + email: str = pydantic.Field() + """ + The email you have defined for the contact who is being added as a participant. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/customer_request_intercom_user_id.py b/src/intercom/types/customer_request_intercom_user_id.py new file mode 100644 index 00000000..355cd818 --- /dev/null +++ b/src/intercom/types/customer_request_intercom_user_id.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CustomerRequestIntercomUserId(UncheckedBaseModel): + intercom_user_id: str = pydantic.Field() + """ + The identifier for the contact as given by Intercom. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/customer_request_user_id.py b/src/intercom/types/customer_request_user_id.py new file mode 100644 index 00000000..4f0f1505 --- /dev/null +++ b/src/intercom/types/customer_request_user_id.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class CustomerRequestUserId(UncheckedBaseModel): + user_id: str = pydantic.Field() + """ + The external_id you have defined for the contact who is being added as a participant. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/data_attribute_list.py b/src/intercom/types/data_attribute_list.py new file mode 100644 index 00000000..2a7b137e --- /dev/null +++ b/src/intercom/types/data_attribute_list.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from ..data_attributes.types.data_attribute import DataAttribute + + +class DataAttributeList(UncheckedBaseModel): + """ + A list of all data attributes belonging to a workspace for contacts, companies or conversations. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + data: typing.Optional[typing.List[DataAttribute]] = pydantic.Field(default=None) + """ + A list of data attributes + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/data_event_list.py b/src/intercom/types/data_event_list.py new file mode 100644 index 00000000..f69a4646 --- /dev/null +++ b/src/intercom/types/data_event_list.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from ..data_events.types.data_event import DataEvent +from .data_event_list_pages import DataEventListPages + + +class DataEventList(UncheckedBaseModel): + """ + This will return a list of data events for the App. + """ + + type: typing.Optional[typing.Literal["event.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + events: typing.Optional[typing.List[DataEvent]] = pydantic.Field(default=None) + """ + A list of data events + """ + + pages: typing.Optional[DataEventListPages] = pydantic.Field(default=None) + """ + Pagination + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/data_event_list_pages.py b/src/intercom/types/data_event_list_pages.py new file mode 100644 index 00000000..f4634d79 --- /dev/null +++ b/src/intercom/types/data_event_list_pages.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class DataEventListPages(UncheckedBaseModel): + """ + Pagination + """ + + next: typing.Optional[str] = None + since: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/data_event_summary.py b/src/intercom/types/data_event_summary.py new file mode 100644 index 00000000..4dab92b9 --- /dev/null +++ b/src/intercom/types/data_event_summary.py @@ -0,0 +1,48 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .data_event_summary_item import DataEventSummaryItem + + +class DataEventSummary(UncheckedBaseModel): + """ + This will return a summary of data events for the App. + """ + + type: typing.Optional[typing.Literal["event.summary"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + The email address of the user + """ + + intercom_user_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom user ID of the user + """ + + user_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The user ID of the user + """ + + events: typing.List[typing.Optional[DataEventSummaryItem]] = pydantic.Field() + """ + A summary of data events + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/data_event_summary_item.py b/src/intercom/types/data_event_summary_item.py new file mode 100644 index 00000000..aa3842f3 --- /dev/null +++ b/src/intercom/types/data_event_summary_item.py @@ -0,0 +1,47 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class DataEventSummaryItem(UncheckedBaseModel): + """ + This will return a summary of a data event for the App. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the event + """ + + first: typing.Optional[str] = pydantic.Field(default=None) + """ + The first time the event was sent + """ + + last: typing.Optional[str] = pydantic.Field(default=None) + """ + The last time the event was sent + """ + + count: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of times the event was sent + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + The description of the event + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/data_export_csv.py b/src/intercom/types/data_export_csv.py new file mode 100644 index 00000000..40518e84 --- /dev/null +++ b/src/intercom/types/data_export_csv.py @@ -0,0 +1,152 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class DataExportCsv(UncheckedBaseModel): + """ + A CSV output file + """ + + user_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The user_id of the user who was sent the message. + """ + + user_external_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The external_user_id of the user who was sent the message + """ + + company_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The company ID of the user in relation to the message that was sent. Will return -1 if no company is present. + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + The users email who was sent the message. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The full name of the user receiving the message + """ + + ruleset_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the message. + """ + + content_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The specific content that was received. In an A/B test each version has its own Content ID. + """ + + content_type: typing.Optional[str] = pydantic.Field(default=None) + """ + Email, Chat, Post etc. + """ + + content_title: typing.Optional[str] = pydantic.Field(default=None) + """ + The title of the content you see in your Intercom workspace. + """ + + ruleset_version_id: typing.Optional[str] = pydantic.Field(default=None) + """ + As you edit content we record new versions. This ID can help you determine which version of a piece of content that was received. + """ + + receipt_id: typing.Optional[str] = pydantic.Field(default=None) + """ + ID for this receipt. Will be included with any related stats in other files to identify this specific delivery of a message. + """ + + received_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Timestamp for when the receipt was recorded. + """ + + series_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the series that this content is part of. Will return -1 if not part of a series. + """ + + series_title: typing.Optional[str] = pydantic.Field(default=None) + """ + The title of the series that this content is part of. + """ + + node_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the series node that this ruleset is associated with. Each block in a series has a corresponding node_id. + """ + + first_reply: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time a user replied to this message if the content was able to receive replies. + """ + + first_completion: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time a user completed this message if the content was able to be completed e.g. Tours, Surveys. + """ + + first_series_completion: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time the series this message was a part of was completed by the user. + """ + + first_series_disengagement: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time the series this message was a part of was disengaged by the user. + """ + + first_series_exit: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time the series this message was a part of was exited by the user. + """ + + first_goal_success: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time the user met this messages associated goal if one exists. + """ + + first_open: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time the user opened this message. + """ + + first_click: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time the series the user clicked on a link within this message. + """ + + first_dismisall: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time the series the user dismissed this message. + """ + + first_unsubscribe: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time the user unsubscribed from this message. + """ + + first_hard_bounce: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time this message hard bounced for this user + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/datetime.py b/src/intercom/types/datetime.py new file mode 100644 index 00000000..97bfdacc --- /dev/null +++ b/src/intercom/types/datetime.py @@ -0,0 +1,6 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +Datetime = typing.Union[dt.datetime, int] diff --git a/src/intercom/types/deleted_article_object.py b/src/intercom/types/deleted_article_object.py new file mode 100644 index 00000000..40038e7a --- /dev/null +++ b/src/intercom/types/deleted_article_object.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class DeletedArticleObject(UncheckedBaseModel): + """ + Response returned when an object is deleted + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the article which you provided in the URL. + """ + + object: typing.Optional[typing.Literal["article"]] = pydantic.Field(default=None) + """ + The type of object which was deleted. - article + """ + + deleted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the article was deleted successfully or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/deleted_collection_object.py b/src/intercom/types/deleted_collection_object.py new file mode 100644 index 00000000..1628b9fc --- /dev/null +++ b/src/intercom/types/deleted_collection_object.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class DeletedCollectionObject(UncheckedBaseModel): + """ + Response returned when an object is deleted + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the collection which you provided in the URL. + """ + + object: typing.Optional[typing.Literal["collection"]] = pydantic.Field(default=None) + """ + The type of object which was deleted. - `collection` + """ + + deleted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the collection was deleted successfully or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/deleted_company_object.py b/src/intercom/types/deleted_company_object.py new file mode 100644 index 00000000..16ad8eff --- /dev/null +++ b/src/intercom/types/deleted_company_object.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class DeletedCompanyObject(UncheckedBaseModel): + """ + Response returned when an object is deleted + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the company which is given by Intercom. + """ + + object: typing.Optional[typing.Literal["company"]] = pydantic.Field(default=None) + """ + The type of object which was deleted. - `company` + """ + + deleted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the company was deleted successfully or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/deleted_internal_article_object.py b/src/intercom/types/deleted_internal_article_object.py new file mode 100644 index 00000000..848b27e2 --- /dev/null +++ b/src/intercom/types/deleted_internal_article_object.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class DeletedInternalArticleObject(UncheckedBaseModel): + """ + Response returned when an object is deleted + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the internal article which you provided in the URL. + """ + + object: typing.Optional[typing.Literal["internal_article"]] = pydantic.Field(default=None) + """ + The type of object which was deleted. - internal_article + """ + + deleted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the internal article was deleted successfully or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/deleted_object.py b/src/intercom/types/deleted_object.py new file mode 100644 index 00000000..dbf1f77b --- /dev/null +++ b/src/intercom/types/deleted_object.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class DeletedObject(UncheckedBaseModel): + """ + Response returned when an object is deleted + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the news item which you provided in the URL. + """ + + object: typing.Optional[typing.Literal["news-item"]] = pydantic.Field(default=None) + """ + The type of object which was deleted - news-item. + """ + + deleted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the news item was deleted successfully or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/email_address_header.py b/src/intercom/types/email_address_header.py new file mode 100644 index 00000000..9e990ad8 --- /dev/null +++ b/src/intercom/types/email_address_header.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class EmailAddressHeader(UncheckedBaseModel): + """ + Contains data for an email address header for a conversation part that was sent as an email. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of email address header + """ + + email_address: typing.Optional[str] = pydantic.Field(default=None) + """ + The email address + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name associated with the email address + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/email_message_metadata.py b/src/intercom/types/email_message_metadata.py new file mode 100644 index 00000000..eb80c07e --- /dev/null +++ b/src/intercom/types/email_message_metadata.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .email_address_header import EmailAddressHeader + + +class EmailMessageMetadata(UncheckedBaseModel): + """ + Contains metadata if the message was sent as an email + """ + + subject: typing.Optional[str] = pydantic.Field(default=None) + """ + The subject of the email + """ + + email_address_headers: typing.Optional[typing.List[EmailAddressHeader]] = pydantic.Field(default=None) + """ + A list of an email address headers. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/error.py b/src/intercom/types/error.py new file mode 100644 index 00000000..a374acf3 --- /dev/null +++ b/src/intercom/types/error.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .error_errors_item import ErrorErrorsItem + + +class Error(UncheckedBaseModel): + """ + The API will return an Error List for a failed request, which will contain one or more Error objects. + """ + + type: typing.Literal["error.list"] = pydantic.Field(default="error.list") + """ + The type is error.list + """ + + request_id: typing.Optional[str] = pydantic.Field(default=None) + """ + + """ + + errors: typing.List[ErrorErrorsItem] = pydantic.Field() + """ + An array of one or more error objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/error_errors_item.py b/src/intercom/types/error_errors_item.py new file mode 100644 index 00000000..85607be0 --- /dev/null +++ b/src/intercom/types/error_errors_item.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class ErrorErrorsItem(UncheckedBaseModel): + code: str = pydantic.Field() + """ + A string indicating the kind of error, used to further qualify the HTTP response code + """ + + message: typing.Optional[str] = pydantic.Field(default=None) + """ + Optional. Human readable description of the error. + """ + + field: typing.Optional[str] = pydantic.Field(default=None) + """ + Optional. Used to identify a particular field or query parameter that was in error. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/event_details.py b/src/intercom/types/event_details.py new file mode 100644 index 00000000..2bef2220 --- /dev/null +++ b/src/intercom/types/event_details.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .conversation_attribute_updated_by_admin import ConversationAttributeUpdatedByAdmin +from .conversation_attribute_updated_by_workflow import ConversationAttributeUpdatedByWorkflow +from .custom_action_finished import CustomActionFinished +from .custom_action_started import CustomActionStarted +from .operator_workflow_event import OperatorWorkflowEvent + +EventDetails = typing.Union[ + ConversationAttributeUpdatedByWorkflow, + ConversationAttributeUpdatedByAdmin, + CustomActionStarted, + CustomActionFinished, + OperatorWorkflowEvent, +] diff --git a/src/intercom/types/file_attribute.py b/src/intercom/types/file_attribute.py new file mode 100644 index 00000000..e63f4ccf --- /dev/null +++ b/src/intercom/types/file_attribute.py @@ -0,0 +1,53 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class FileAttribute(UncheckedBaseModel): + """ + The value describing a file upload set for a custom attribute + """ + + type: typing.Optional[str] = None + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the file + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The url of the file. This is a temporary URL and will expire after 30 minutes. + """ + + content_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of file + """ + + filesize: typing.Optional[int] = pydantic.Field(default=None) + """ + The size of the file in bytes + """ + + width: typing.Optional[int] = pydantic.Field(default=None) + """ + The width of the file in pixels, if applicable + """ + + height: typing.Optional[int] = pydantic.Field(default=None) + """ + The height of the file in pixels, if applicable + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/group_content.py b/src/intercom/types/group_content.py new file mode 100644 index 00000000..aa532290 --- /dev/null +++ b/src/intercom/types/group_content.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class GroupContent(UncheckedBaseModel): + """ + The Content of a Group. + """ + + type: typing.Optional[typing.Literal["group_content"]] = pydantic.Field(default=None) + """ + The type of object - `group_content` . + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the collection or section. + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + The description of the collection. Only available for collections. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/group_translated_content.py b/src/intercom/types/group_translated_content.py new file mode 100644 index 00000000..808e1334 --- /dev/null +++ b/src/intercom/types/group_translated_content.py @@ -0,0 +1,221 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.serialization import FieldMetadata +from ..core.unchecked_base_model import UncheckedBaseModel +from .group_content import GroupContent + + +class GroupTranslatedContent(UncheckedBaseModel): + """ + The Translated Content of an Group. The keys are the locale codes and the values are the translated content of the Group. + """ + + type: typing.Optional[typing.Literal["group_translated_content"]] = pydantic.Field(default=None) + """ + The type of object - group_translated_content. + """ + + ar: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Arabic + """ + + bg: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Bulgarian + """ + + bs: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Bosnian + """ + + ca: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Catalan + """ + + cs: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Czech + """ + + da: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Danish + """ + + de: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in German + """ + + el: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Greek + """ + + en: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in English + """ + + es: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Spanish + """ + + et: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Estonian + """ + + fi: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Finnish + """ + + fr: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in French + """ + + he: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Hebrew + """ + + hr: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Croatian + """ + + hu: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Hungarian + """ + + id: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Indonesian + """ + + it: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Italian + """ + + ja: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Japanese + """ + + ko: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Korean + """ + + lt: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Lithuanian + """ + + lv: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Latvian + """ + + mn: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Mongolian + """ + + nb: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Norwegian + """ + + nl: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Dutch + """ + + pl: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Polish + """ + + pt: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Portuguese (Portugal) + """ + + ro: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Romanian + """ + + ru: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Russian + """ + + sl: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Slovenian + """ + + sr: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Serbian + """ + + sv: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Swedish + """ + + tr: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Turkish + """ + + vi: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Vietnamese + """ + + pt_br: typing_extensions.Annotated[typing.Optional[GroupContent], FieldMetadata(alias="pt-BR")] = pydantic.Field( + default=None + ) + """ + The content of the group in Portuguese (Brazil) + """ + + zh_cn: typing_extensions.Annotated[typing.Optional[GroupContent], FieldMetadata(alias="zh-CN")] = pydantic.Field( + default=None + ) + """ + The content of the group in Chinese (China) + """ + + zh_tw: typing_extensions.Annotated[typing.Optional[GroupContent], FieldMetadata(alias="zh-TW")] = pydantic.Field( + default=None + ) + """ + The content of the group in Chinese (Taiwan) + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/internal_article_list.py b/src/intercom/types/internal_article_list.py new file mode 100644 index 00000000..760b1ea5 --- /dev/null +++ b/src/intercom/types/internal_article_list.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from ..internal_articles.types.internal_article_list_item import InternalArticleListItem +from .cursor_pages import CursorPages + + +class InternalArticleList(UncheckedBaseModel): + """ + This will return a list of internal articles for the App. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object - `list`. + """ + + pages: typing.Optional[CursorPages] = None + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + A count of the total number of internal articles. + """ + + data: typing.Optional[typing.List[InternalArticleListItem]] = pydantic.Field(default=None) + """ + An array of Internal Article objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/linked_object.py b/src/intercom/types/linked_object.py new file mode 100644 index 00000000..afa218f4 --- /dev/null +++ b/src/intercom/types/linked_object.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .linked_object_category import LinkedObjectCategory +from .linked_object_type import LinkedObjectType + + +class LinkedObject(UncheckedBaseModel): + """ + A linked conversation or ticket. + """ + + type: typing.Optional[LinkedObjectType] = pydantic.Field(default=None) + """ + ticket or conversation + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the linked object + """ + + category: typing.Optional[LinkedObjectCategory] = pydantic.Field(default=None) + """ + Category of the Linked Ticket Object. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/linked_object_category.py b/src/intercom/types/linked_object_category.py new file mode 100644 index 00000000..bea12bf0 --- /dev/null +++ b/src/intercom/types/linked_object_category.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +LinkedObjectCategory = typing.Union[typing.Literal["Customer", "Back-office", "Tracker"], typing.Any] diff --git a/src/intercom/types/linked_object_list.py b/src/intercom/types/linked_object_list.py new file mode 100644 index 00000000..d3221de8 --- /dev/null +++ b/src/intercom/types/linked_object_list.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .linked_object import LinkedObject + + +class LinkedObjectList(UncheckedBaseModel): + """ + An object containing metadata about linked conversations and linked tickets. Up to 1000 can be returned. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + Always list. + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + The total number of linked objects. + """ + + has_more: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether or not there are more linked objects than returned. + """ + + data: typing.Optional[typing.List[LinkedObject]] = pydantic.Field(default=None) + """ + An array containing the linked conversations and linked tickets. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/linked_object_type.py b/src/intercom/types/linked_object_type.py new file mode 100644 index 00000000..16749f1f --- /dev/null +++ b/src/intercom/types/linked_object_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +LinkedObjectType = typing.Union[typing.Literal["ticket", "conversation"], typing.Any] diff --git a/src/intercom/types/metadata.py b/src/intercom/types/metadata.py new file mode 100644 index 00000000..cf4ab6d4 --- /dev/null +++ b/src/intercom/types/metadata.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..unstable.types.conversation_part_metadata import ConversationPartMetadata + +Metadata = ConversationPartMetadata diff --git a/src/intercom/types/multiple_filter_search_request.py b/src/intercom/types/multiple_filter_search_request.py new file mode 100644 index 00000000..84a12099 --- /dev/null +++ b/src/intercom/types/multiple_filter_search_request.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, update_forward_refs +from ..core.unchecked_base_model import UncheckedBaseModel + + +class MultipleFilterSearchRequest(UncheckedBaseModel): + """ + Search using Intercoms Search APIs with more than one filter. + """ + + operator: typing.Optional[MultipleFilterSearchRequestOperator] = pydantic.Field(default=None) + """ + An operator to allow boolean inspection between multiple fields. + """ + + value: typing.Optional["MultipleFilterSearchRequestValue"] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +from .single_filter_search_request_value import SingleFilterSearchRequestValue # noqa: E402, I001 +from .single_filter_search_request_value_two_item import SingleFilterSearchRequestValueTwoItem # noqa: E402, I001 +from .single_filter_search_request_operator import SingleFilterSearchRequestOperator # noqa: E402, I001 +from .single_filter_search_request import SingleFilterSearchRequest # noqa: E402, I001 +from .multiple_filter_search_request_operator import MultipleFilterSearchRequestOperator # noqa: E402, I001 +from .multiple_filter_search_request_value import MultipleFilterSearchRequestValue # noqa: E402, I001 + +update_forward_refs( + MultipleFilterSearchRequest, + MultipleFilterSearchRequestOperator=MultipleFilterSearchRequestOperator, + MultipleFilterSearchRequestValue=MultipleFilterSearchRequestValue, + SingleFilterSearchRequest=SingleFilterSearchRequest, + SingleFilterSearchRequestOperator=SingleFilterSearchRequestOperator, + SingleFilterSearchRequestValue=SingleFilterSearchRequestValue, + SingleFilterSearchRequestValueTwoItem=SingleFilterSearchRequestValueTwoItem, +) diff --git a/src/intercom/types/multiple_filter_search_request_operator.py b/src/intercom/types/multiple_filter_search_request_operator.py new file mode 100644 index 00000000..38d46593 --- /dev/null +++ b/src/intercom/types/multiple_filter_search_request_operator.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +MultipleFilterSearchRequestOperator = typing.Union[typing.Literal["AND", "OR"], typing.Any] diff --git a/src/intercom/types/multiple_filter_search_request_value.py b/src/intercom/types/multiple_filter_search_request_value.py new file mode 100644 index 00000000..16a7c594 --- /dev/null +++ b/src/intercom/types/multiple_filter_search_request_value.py @@ -0,0 +1,13 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +from .single_filter_search_request import SingleFilterSearchRequest + +if typing.TYPE_CHECKING: + from .multiple_filter_search_request import MultipleFilterSearchRequest +MultipleFilterSearchRequestValue = typing.Union[ + typing.List["MultipleFilterSearchRequest"], typing.List[SingleFilterSearchRequest] +] diff --git a/src/intercom/types/news_item_request.py b/src/intercom/types/news_item_request.py new file mode 100644 index 00000000..284e24a2 --- /dev/null +++ b/src/intercom/types/news_item_request.py @@ -0,0 +1,64 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from ..news.types.newsfeed_assignment import NewsfeedAssignment +from .news_item_request_state import NewsItemRequestState + + +class NewsItemRequest(UncheckedBaseModel): + """ + A News Item is a content type in Intercom enabling you to announce product updates, company news, promotions, events and more with your customers. + """ + + title: str = pydantic.Field() + """ + The title of the news item. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The news item body, which may contain HTML. + """ + + sender_id: int = pydantic.Field() + """ + The id of the sender of the news item. Must be a teammate on the workspace. + """ + + state: typing.Optional[NewsItemRequestState] = pydantic.Field(default=None) + """ + News items will not be visible to your users in the assigned newsfeeds until they are set live. + """ + + deliver_silently: typing.Optional[bool] = pydantic.Field(default=None) + """ + When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + """ + + labels: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + Label names displayed to users to categorize the news item. + """ + + reactions: typing.Optional[typing.List[typing.Optional[str]]] = pydantic.Field(default=None) + """ + Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + """ + + newsfeed_assignments: typing.Optional[typing.List[NewsfeedAssignment]] = pydantic.Field(default=None) + """ + A list of newsfeed_assignments to assign to the specified newsfeed. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/news_item_request_state.py b/src/intercom/types/news_item_request_state.py new file mode 100644 index 00000000..0c6a2330 --- /dev/null +++ b/src/intercom/types/news_item_request_state.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +NewsItemRequestState = typing.Union[typing.Literal["draft", "live"], typing.Any] diff --git a/src/intercom/types/not_found_error_body.py b/src/intercom/types/not_found_error_body.py new file mode 100644 index 00000000..b19fe725 --- /dev/null +++ b/src/intercom/types/not_found_error_body.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .not_found_error_body_errors_item import NotFoundErrorBodyErrorsItem + + +class NotFoundErrorBody(UncheckedBaseModel): + type: str = pydantic.Field() + """ + The type is error.list + """ + + request_id: typing.Optional[str] = pydantic.Field(default=None) + """ + + """ + + errors: typing.List[NotFoundErrorBodyErrorsItem] = pydantic.Field() + """ + An array of one or more error objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/not_found_error_body_errors_item.py b/src/intercom/types/not_found_error_body_errors_item.py new file mode 100644 index 00000000..ca80ecf4 --- /dev/null +++ b/src/intercom/types/not_found_error_body_errors_item.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class NotFoundErrorBodyErrorsItem(UncheckedBaseModel): + code: str = pydantic.Field() + """ + ticket_not_found + """ + + message: typing.Optional[str] = pydantic.Field(default=None) + """ + Ticket not found + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/note_list.py b/src/intercom/types/note_list.py new file mode 100644 index 00000000..7ba90ced --- /dev/null +++ b/src/intercom/types/note_list.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from ..notes.types.note import Note + + +class NoteList(UncheckedBaseModel): + """ + A paginated list of notes associated with a contact. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `list`. + """ + + data: typing.Optional[typing.List[Note]] = pydantic.Field(default=None) + """ + An array of notes. + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + A count of the total number of notes. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/offset_pages.py b/src/intercom/types/offset_pages.py new file mode 100644 index 00000000..b340bc86 --- /dev/null +++ b/src/intercom/types/offset_pages.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class OffsetPages(UncheckedBaseModel): + type: typing.Literal["offset_pages"] = pydantic.Field(default="offset_pages") + """ + the type of object `offset_pages` + """ + + page: typing.Optional[int] = pydantic.Field(default=None) + """ + The current offset + """ + + next: typing.Optional[str] = None + per_page: typing.Optional[int] = pydantic.Field(default=None) + """ + Number of results per page + """ + + total_pages: typing.Optional[int] = pydantic.Field(default=None) + """ + Total number of pages + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/open_conversation_request.py b/src/intercom/types/open_conversation_request.py new file mode 100644 index 00000000..4f15d73b --- /dev/null +++ b/src/intercom/types/open_conversation_request.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class OpenConversationRequest(UncheckedBaseModel): + """ + Payload of the request to open a conversation + """ + + admin_id: str = pydantic.Field() + """ + The id of the admin who is performing the action. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/operator_workflow_event.py b/src/intercom/types/operator_workflow_event.py new file mode 100644 index 00000000..d9ec4975 --- /dev/null +++ b/src/intercom/types/operator_workflow_event.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .operator_workflow_event_event import OperatorWorkflowEventEvent +from .operator_workflow_event_workflow import OperatorWorkflowEventWorkflow + + +class OperatorWorkflowEvent(UncheckedBaseModel): + """ + Contains details about name of the workflow for conversation part type operator_workflow_event. + """ + + workflow: typing.Optional[OperatorWorkflowEventWorkflow] = None + event: typing.Optional[OperatorWorkflowEventEvent] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/operator_workflow_event_event.py b/src/intercom/types/operator_workflow_event_event.py new file mode 100644 index 00000000..10bf3efa --- /dev/null +++ b/src/intercom/types/operator_workflow_event_event.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class OperatorWorkflowEventEvent(UncheckedBaseModel): + type: typing.Optional[str] = pydantic.Field(default=None) + """ + Type of the workflow event initiated + """ + + result: typing.Optional[str] = pydantic.Field(default=None) + """ + Result of the workflow event + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/operator_workflow_event_workflow.py b/src/intercom/types/operator_workflow_event_workflow.py new file mode 100644 index 00000000..09526584 --- /dev/null +++ b/src/intercom/types/operator_workflow_event_workflow.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class OperatorWorkflowEventWorkflow(UncheckedBaseModel): + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the workflow + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/pages_link.py b/src/intercom/types/pages_link.py new file mode 100644 index 00000000..ccd818f4 --- /dev/null +++ b/src/intercom/types/pages_link.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class PagesLink(UncheckedBaseModel): + """ + The majority of list resources in the API are paginated to allow clients to traverse data over multiple requests. + + Their responses are likely to contain a pages object that hosts pagination links which a client can use to paginate through the data without having to construct a query. The link relations for the pages field are as follows. + """ + + type: typing.Optional[typing.Literal["pages"]] = None + page: typing.Optional[int] = None + next: typing.Optional[str] = pydantic.Field(default=None) + """ + A link to the next page of results. A response that does not contain a next link does not have further data to fetch. + """ + + per_page: typing.Optional[int] = None + total_pages: typing.Optional[int] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/paginated_response.py b/src/intercom/types/paginated_response.py new file mode 100644 index 00000000..787bff82 --- /dev/null +++ b/src/intercom/types/paginated_response.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .cursor_pages import CursorPages +from .paginated_response_data_item import PaginatedResponseDataItem +from .paginated_response_type import PaginatedResponseType + + +class PaginatedResponse(UncheckedBaseModel): + """ + Paginated Response + """ + + type: typing.Optional[PaginatedResponseType] = pydantic.Field(default=None) + """ + The type of object + """ + + pages: typing.Optional[CursorPages] = None + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + A count of the total number of objects. + """ + + data: typing.Optional[typing.List[PaginatedResponseDataItem]] = pydantic.Field(default=None) + """ + An array of Objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/paginated_response_data_item.py b/src/intercom/types/paginated_response_data_item.py new file mode 100644 index 00000000..0052b2f5 --- /dev/null +++ b/src/intercom/types/paginated_response_data_item.py @@ -0,0 +1,61 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import pydantic +import typing_extensions +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel, UnionMetadata +from ..news.types.news_item_state import NewsItemState +from ..news.types.newsfeed_assignment import NewsfeedAssignment + + +class PaginatedResponseDataItem_NewsItem(UncheckedBaseModel): + type: typing.Literal["news-item"] = "news-item" + id: typing.Optional[str] = None + workspace_id: typing.Optional[str] = None + title: typing.Optional[str] = None + body: typing.Optional[str] = None + sender_id: typing.Optional[int] = None + state: typing.Optional[NewsItemState] = None + newsfeed_assignments: typing.Optional[typing.List[NewsfeedAssignment]] = None + labels: typing.Optional[typing.List[typing.Optional[str]]] = None + cover_image_url: typing.Optional[str] = None + reactions: typing.Optional[typing.List[typing.Optional[str]]] = None + deliver_silently: typing.Optional[bool] = None + created_at: typing.Optional[int] = None + updated_at: typing.Optional[int] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class PaginatedResponseDataItem_Newsfeed(UncheckedBaseModel): + type: typing.Literal["newsfeed"] = "newsfeed" + id: typing.Optional[str] = None + name: typing.Optional[str] = None + created_at: typing.Optional[int] = None + updated_at: typing.Optional[int] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +PaginatedResponseDataItem = typing_extensions.Annotated[ + typing.Union[PaginatedResponseDataItem_NewsItem, PaginatedResponseDataItem_Newsfeed], + UnionMetadata(discriminant="type"), +] diff --git a/src/intercom/types/paginated_response_type.py b/src/intercom/types/paginated_response_type.py new file mode 100644 index 00000000..04cce509 --- /dev/null +++ b/src/intercom/types/paginated_response_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +PaginatedResponseType = typing.Union[typing.Literal["list", "conversation.list"], typing.Any] diff --git a/src/intercom/types/part_attachment.py b/src/intercom/types/part_attachment.py new file mode 100644 index 00000000..7917095d --- /dev/null +++ b/src/intercom/types/part_attachment.py @@ -0,0 +1,57 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class PartAttachment(UncheckedBaseModel): + """ + The file attached to a part + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of attachment + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the attachment + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL of the attachment + """ + + content_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The content type of the attachment + """ + + filesize: typing.Optional[int] = pydantic.Field(default=None) + """ + The size of the attachment + """ + + width: typing.Optional[int] = pydantic.Field(default=None) + """ + The width of the attachment + """ + + height: typing.Optional[int] = pydantic.Field(default=None) + """ + The height of the attachment + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/phone_switch.py b/src/intercom/types/phone_switch.py new file mode 100644 index 00000000..7f20b7aa --- /dev/null +++ b/src/intercom/types/phone_switch.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class PhoneSwitch(UncheckedBaseModel): + """ + Phone Switch Response + """ + + type: typing.Optional[typing.Literal["phone_call_redirect"]] = pydantic.Field(default=None) + """ + + """ + + phone: typing.Optional[str] = pydantic.Field(default=None) + """ + Phone number in E.164 format, that has received the SMS to continue the conversation in the Messenger. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/quick_reply_option.py b/src/intercom/types/quick_reply_option.py new file mode 100644 index 00000000..15a918d6 --- /dev/null +++ b/src/intercom/types/quick_reply_option.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.serialization import FieldMetadata +from ..core.unchecked_base_model import UncheckedBaseModel + + +class QuickReplyOption(UncheckedBaseModel): + text: str = pydantic.Field() + """ + The text to display in this quick reply option. + """ + + uuid_: typing_extensions.Annotated[str, FieldMetadata(alias="uuid")] = pydantic.Field() + """ + A unique identifier for this quick reply option. This value will be available within the metadata of the comment conversation part that is created when a user clicks on this reply option. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/recipient.py b/src/intercom/types/recipient.py new file mode 100644 index 00000000..e68adf07 --- /dev/null +++ b/src/intercom/types/recipient.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .recipient_type import RecipientType + + +class Recipient(UncheckedBaseModel): + """ + A recipient of a message + """ + + type: RecipientType = pydantic.Field() + """ + The role associated to the contact - `user` or `lead`. + """ + + id: str = pydantic.Field() + """ + The identifier for the contact which is given by Intercom. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/recipient_type.py b/src/intercom/types/recipient_type.py new file mode 100644 index 00000000..f423fd01 --- /dev/null +++ b/src/intercom/types/recipient_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +RecipientType = typing.Union[typing.Literal["user", "lead"], typing.Any] diff --git a/src/intercom/types/redact_conversation_request.py b/src/intercom/types/redact_conversation_request.py new file mode 100644 index 00000000..54e31071 --- /dev/null +++ b/src/intercom/types/redact_conversation_request.py @@ -0,0 +1,46 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import pydantic +import typing_extensions +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel, UnionMetadata + + +class RedactConversationRequest_ConversationPart(UncheckedBaseModel): + type: typing.Literal["conversation_part"] = "conversation_part" + conversation_id: str + conversation_part_id: str + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class RedactConversationRequest_Source(UncheckedBaseModel): + type: typing.Literal["source"] = "source" + conversation_id: str + source_id: str + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +RedactConversationRequest = typing_extensions.Annotated[ + typing.Union[RedactConversationRequest_ConversationPart, RedactConversationRequest_Source], + UnionMetadata(discriminant="type"), +] diff --git a/src/intercom/types/redact_conversation_request_conversation_part.py b/src/intercom/types/redact_conversation_request_conversation_part.py new file mode 100644 index 00000000..2be9204b --- /dev/null +++ b/src/intercom/types/redact_conversation_request_conversation_part.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class RedactConversationRequestConversationPart(UncheckedBaseModel): + """ + Payload of the request to redact a conversation part + """ + + conversation_id: str = pydantic.Field() + """ + The id of the conversation. + """ + + conversation_part_id: str = pydantic.Field() + """ + The id of the conversation_part. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/redact_conversation_request_source.py b/src/intercom/types/redact_conversation_request_source.py new file mode 100644 index 00000000..f5c38667 --- /dev/null +++ b/src/intercom/types/redact_conversation_request_source.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class RedactConversationRequestSource(UncheckedBaseModel): + """ + Payload of the request to redact a conversation source + """ + + conversation_id: str = pydantic.Field() + """ + The id of the conversation. + """ + + source_id: str = pydantic.Field() + """ + The id of the source. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/reference.py b/src/intercom/types/reference.py new file mode 100644 index 00000000..0c8ad774 --- /dev/null +++ b/src/intercom/types/reference.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class Reference(UncheckedBaseModel): + """ + reference to another object + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/reply_conversation_request.py b/src/intercom/types/reply_conversation_request.py new file mode 100644 index 00000000..e90aa4ca --- /dev/null +++ b/src/intercom/types/reply_conversation_request.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .admin_reply_conversation_request import AdminReplyConversationRequest +from .contact_reply_conversation_request import ContactReplyConversationRequest + +ReplyConversationRequest = typing.Union[ContactReplyConversationRequest, AdminReplyConversationRequest] diff --git a/src/intercom/types/search_request.py b/src/intercom/types/search_request.py new file mode 100644 index 00000000..c67e7504 --- /dev/null +++ b/src/intercom/types/search_request.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, update_forward_refs +from ..core.unchecked_base_model import UncheckedBaseModel +from .search_request_query import SearchRequestQuery +from .starting_after_paging import StartingAfterPaging + + +class SearchRequest(UncheckedBaseModel): + """ + Search using Intercoms Search APIs. + """ + + query: SearchRequestQuery + pagination: typing.Optional[StartingAfterPaging] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +update_forward_refs(SearchRequest) diff --git a/src/intercom/types/search_request_query.py b/src/intercom/types/search_request_query.py new file mode 100644 index 00000000..c338cbe2 --- /dev/null +++ b/src/intercom/types/search_request_query.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .multiple_filter_search_request import MultipleFilterSearchRequest +from .single_filter_search_request import SingleFilterSearchRequest + +SearchRequestQuery = typing.Union[SingleFilterSearchRequest, MultipleFilterSearchRequest] diff --git a/src/intercom/types/segment_list.py b/src/intercom/types/segment_list.py new file mode 100644 index 00000000..e954e30e --- /dev/null +++ b/src/intercom/types/segment_list.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from ..segments.types.segment import Segment + + +class SegmentList(UncheckedBaseModel): + """ + This will return a list of Segment Objects. The result may also have a pages object if the response is paginated. + """ + + type: typing.Optional[typing.Literal["segment.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + segments: typing.Optional[typing.List[Segment]] = pydantic.Field(default=None) + """ + A list of Segment objects + """ + + pages: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + A pagination object, which may be empty, indicating no further pages to fetch. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/single_filter_search_request.py b/src/intercom/types/single_filter_search_request.py new file mode 100644 index 00000000..8f17533c --- /dev/null +++ b/src/intercom/types/single_filter_search_request.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .single_filter_search_request_operator import SingleFilterSearchRequestOperator +from .single_filter_search_request_value import SingleFilterSearchRequestValue + + +class SingleFilterSearchRequest(UncheckedBaseModel): + """ + Search using Intercoms Search APIs with a single filter. + """ + + field: typing.Optional[str] = pydantic.Field(default=None) + """ + The accepted field that you want to search on. + """ + + operator: typing.Optional[SingleFilterSearchRequestOperator] = pydantic.Field(default=None) + """ + The accepted operators you can use to define how you want to search for the value. + """ + + value: typing.Optional[SingleFilterSearchRequestValue] = pydantic.Field(default=None) + """ + The value that you want to search on. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/single_filter_search_request_operator.py b/src/intercom/types/single_filter_search_request_operator.py new file mode 100644 index 00000000..3f4667de --- /dev/null +++ b/src/intercom/types/single_filter_search_request_operator.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +SingleFilterSearchRequestOperator = typing.Union[ + typing.Literal["=", "!=", "IN", "NIN", "<", ">", "~", "!~", "^", "$"], typing.Any +] diff --git a/src/intercom/types/single_filter_search_request_value.py b/src/intercom/types/single_filter_search_request_value.py new file mode 100644 index 00000000..1ac348b9 --- /dev/null +++ b/src/intercom/types/single_filter_search_request_value.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .single_filter_search_request_value_two_item import SingleFilterSearchRequestValueTwoItem + +SingleFilterSearchRequestValue = typing.Union[str, int, typing.List[SingleFilterSearchRequestValueTwoItem]] diff --git a/src/intercom/types/single_filter_search_request_value_two_item.py b/src/intercom/types/single_filter_search_request_value_two_item.py new file mode 100644 index 00000000..e4b1cf4b --- /dev/null +++ b/src/intercom/types/single_filter_search_request_value_two_item.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +SingleFilterSearchRequestValueTwoItem = typing.Union[str, int] diff --git a/src/intercom/types/sla_applied.py b/src/intercom/types/sla_applied.py new file mode 100644 index 00000000..b4b90732 --- /dev/null +++ b/src/intercom/types/sla_applied.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .sla_applied_sla_status import SlaAppliedSlaStatus + + +class SlaApplied(UncheckedBaseModel): + """ + The SLA Applied object contains the details for which SLA has been applied to this conversation. + Important: if there are any canceled sla_events for the conversation - meaning an SLA has been manually removed from a conversation, the sla_status will always be returned as null. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + object type + """ + + sla_name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the SLA as given by the teammate when it was created. + """ + + sla_status: typing.Optional[SlaAppliedSlaStatus] = pydantic.Field(default=None) + """ + SLA statuses: + - `hit`: If there’s at least one hit event in the underlying sla_events table, and no “missed” or “canceled” events for the conversation. + - `missed`: If there are any missed sla_events for the conversation and no canceled events. If there’s even a single missed sla event, the status will always be missed. A missed status is not applied when the SLA expires, only the next time a teammate replies. + - `active`: An SLA has been applied to a conversation, but has not yet been fulfilled. SLA status is active only if there are no “hit, “missed”, or “canceled” events. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/sla_applied_sla_status.py b/src/intercom/types/sla_applied_sla_status.py new file mode 100644 index 00000000..c2ad85af --- /dev/null +++ b/src/intercom/types/sla_applied_sla_status.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +SlaAppliedSlaStatus = typing.Union[typing.Literal["hit", "missed", "cancelled", "active"], typing.Any] diff --git a/src/intercom/types/snooze_conversation_request.py b/src/intercom/types/snooze_conversation_request.py new file mode 100644 index 00000000..811a665e --- /dev/null +++ b/src/intercom/types/snooze_conversation_request.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class SnoozeConversationRequest(UncheckedBaseModel): + """ + Payload of the request to snooze a conversation + """ + + admin_id: str = pydantic.Field() + """ + The id of the admin who is performing the action. + """ + + snoozed_until: int = pydantic.Field() + """ + The time you want the conversation to reopen. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/social_profile.py b/src/intercom/types/social_profile.py new file mode 100644 index 00000000..f1b91d26 --- /dev/null +++ b/src/intercom/types/social_profile.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class SocialProfile(UncheckedBaseModel): + """ + A Social Profile allows you to label your contacts, companies, and conversations and list them using that Social Profile. + """ + + type: typing.Optional[typing.Literal["social_profile"]] = pydantic.Field(default=None) + """ + value is "social_profile" + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the Social media profile + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the Social media profile + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/starting_after_paging.py b/src/intercom/types/starting_after_paging.py new file mode 100644 index 00000000..e2c1a299 --- /dev/null +++ b/src/intercom/types/starting_after_paging.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class StartingAfterPaging(UncheckedBaseModel): + per_page: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of results to fetch per page. + """ + + starting_after: typing.Optional[str] = pydantic.Field(default=None) + """ + The cursor to use in the next request to get the next page of results. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/subscription_type_list.py b/src/intercom/types/subscription_type_list.py new file mode 100644 index 00000000..b0d25969 --- /dev/null +++ b/src/intercom/types/subscription_type_list.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from ..subscription_types.types.subscription_type import SubscriptionType + + +class SubscriptionTypeList(UncheckedBaseModel): + """ + A list of subscription type objects. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + data: typing.Optional[typing.List[SubscriptionType]] = pydantic.Field(default=None) + """ + A list of subscription type objects associated with the workspace . + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/tag_company_request.py b/src/intercom/types/tag_company_request.py new file mode 100644 index 00000000..4af6d20a --- /dev/null +++ b/src/intercom/types/tag_company_request.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .tag_company_request_companies_item import TagCompanyRequestCompaniesItem + + +class TagCompanyRequest(UncheckedBaseModel): + """ + You can tag a single company or a list of companies. + """ + + name: str = pydantic.Field() + """ + The name of the tag, which will be created if not found. + """ + + companies: typing.List[TagCompanyRequestCompaniesItem] = pydantic.Field() + """ + The id or company_id of the company can be passed as input parameters. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/tag_company_request_companies_item.py b/src/intercom/types/tag_company_request_companies_item.py new file mode 100644 index 00000000..b31f48b5 --- /dev/null +++ b/src/intercom/types/tag_company_request_companies_item.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class TagCompanyRequestCompaniesItem(UncheckedBaseModel): + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom defined id representing the company. + """ + + company_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The company id you have defined for the company. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/tag_list.py b/src/intercom/types/tag_list.py new file mode 100644 index 00000000..5a1776c5 --- /dev/null +++ b/src/intercom/types/tag_list.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from ..tags.types.tag import Tag + + +class TagList(UncheckedBaseModel): + """ + A list of tags objects in the workspace. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + data: typing.Optional[typing.List[Tag]] = pydantic.Field(default=None) + """ + A list of tags objects associated with the workspace . + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/tag_multiple_users_request.py b/src/intercom/types/tag_multiple_users_request.py new file mode 100644 index 00000000..4a6a285e --- /dev/null +++ b/src/intercom/types/tag_multiple_users_request.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .tag_multiple_users_request_users_item import TagMultipleUsersRequestUsersItem + + +class TagMultipleUsersRequest(UncheckedBaseModel): + """ + You can tag a list of users. + """ + + name: str = pydantic.Field() + """ + The name of the tag, which will be created if not found. + """ + + users: typing.List[TagMultipleUsersRequestUsersItem] + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/tag_multiple_users_request_users_item.py b/src/intercom/types/tag_multiple_users_request_users_item.py new file mode 100644 index 00000000..91813877 --- /dev/null +++ b/src/intercom/types/tag_multiple_users_request_users_item.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class TagMultipleUsersRequestUsersItem(UncheckedBaseModel): + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom defined id representing the user. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/tags.py b/src/intercom/types/tags.py new file mode 100644 index 00000000..3989c552 --- /dev/null +++ b/src/intercom/types/tags.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from ..tags.types.tag import Tag + + +class Tags(UncheckedBaseModel): + """ + A list of tags objects associated with a conversation + """ + + type: typing.Optional[typing.Literal["tag.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + tags: typing.Optional[typing.List[Tag]] = pydantic.Field(default=None) + """ + A list of tags objects associated with the conversation. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/team_list.py b/src/intercom/types/team_list.py new file mode 100644 index 00000000..52bb0290 --- /dev/null +++ b/src/intercom/types/team_list.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from ..teams.types.team import Team + + +class TeamList(UncheckedBaseModel): + """ + This will return a list of team objects for the App. + """ + + type: typing.Optional[typing.Literal["team.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + teams: typing.Optional[typing.List[Team]] = pydantic.Field(default=None) + """ + A list of team objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/team_priority_level.py b/src/intercom/types/team_priority_level.py new file mode 100644 index 00000000..3bfb1219 --- /dev/null +++ b/src/intercom/types/team_priority_level.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class TeamPriorityLevel(UncheckedBaseModel): + """ + Admin priority levels for teams + """ + + primary_team_ids: typing.Optional[typing.List[int]] = pydantic.Field(default=None) + """ + The primary team ids for the team + """ + + secondary_team_ids: typing.Optional[typing.List[int]] = pydantic.Field(default=None) + """ + The secondary team ids for the team + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/ticket_custom_attributes.py b/src/intercom/types/ticket_custom_attributes.py new file mode 100644 index 00000000..7093b6dd --- /dev/null +++ b/src/intercom/types/ticket_custom_attributes.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketCustomAttributes = typing.Dict[str, typing.Any] diff --git a/src/intercom/types/ticket_list.py b/src/intercom/types/ticket_list.py new file mode 100644 index 00000000..5e6669eb --- /dev/null +++ b/src/intercom/types/ticket_list.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from ..tickets.types.ticket import Ticket +from .cursor_pages import CursorPages + + +class TicketList(UncheckedBaseModel): + """ + Tickets are how you track requests from your users. + """ + + type: typing.Optional[typing.Literal["ticket.list"]] = pydantic.Field(default=None) + """ + Always ticket.list + """ + + tickets: typing.Optional[typing.List[typing.Optional[Ticket]]] = pydantic.Field(default=None) + """ + The list of ticket objects + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + A count of the total number of objects. + """ + + pages: typing.Optional[CursorPages] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/ticket_part_author.py b/src/intercom/types/ticket_part_author.py new file mode 100644 index 00000000..c0538d1b --- /dev/null +++ b/src/intercom/types/ticket_part_author.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .ticket_part_author_type import TicketPartAuthorType + + +class TicketPartAuthor(UncheckedBaseModel): + """ + The author that wrote or triggered the part. Can be a bot, admin, team or user. + """ + + type: typing.Optional[TicketPartAuthorType] = pydantic.Field(default=None) + """ + The type of the author + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the author + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the author + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + The email of the author + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/ticket_part_author_type.py b/src/intercom/types/ticket_part_author_type.py new file mode 100644 index 00000000..1c4aa872 --- /dev/null +++ b/src/intercom/types/ticket_part_author_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketPartAuthorType = typing.Union[typing.Literal["admin", "bot", "team", "user"], typing.Any] diff --git a/src/intercom/types/ticket_parts.py b/src/intercom/types/ticket_parts.py new file mode 100644 index 00000000..230a1bfd --- /dev/null +++ b/src/intercom/types/ticket_parts.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from ..tickets.types.ticket_part import TicketPart + + +class TicketParts(UncheckedBaseModel): + """ + A list of Ticket Part objects for each note and event in the ticket. There is a limit of 500 parts. + """ + + type: typing.Optional[typing.Literal["ticket_part.list"]] = pydantic.Field(default=None) + """ + + """ + + ticket_parts: typing.Optional[typing.List[TicketPart]] = pydantic.Field(default=None) + """ + A list of Ticket Part objects for each ticket. There is a limit of 500 parts. + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/ticket_reply.py b/src/intercom/types/ticket_reply.py new file mode 100644 index 00000000..ec2ab743 --- /dev/null +++ b/src/intercom/types/ticket_reply.py @@ -0,0 +1,66 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .part_attachment import PartAttachment +from .ticket_part_author import TicketPartAuthor +from .ticket_reply_part_type import TicketReplyPartType + + +class TicketReply(UncheckedBaseModel): + """ + A Ticket Part representing a note, comment, or quick_reply on a ticket + """ + + type: typing.Optional[typing.Literal["ticket_part"]] = pydantic.Field(default=None) + """ + Always ticket_part + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the part. + """ + + part_type: typing.Optional[TicketReplyPartType] = pydantic.Field(default=None) + """ + Type of the part + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The message body, which may contain HTML. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the note was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The last time the note was updated. + """ + + author: typing.Optional[TicketPartAuthor] = None + attachments: typing.Optional[typing.List[PartAttachment]] = pydantic.Field(default=None) + """ + A list of attachments for the part. + """ + + redacted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether or not the ticket part has been redacted. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/ticket_reply_part_type.py b/src/intercom/types/ticket_reply_part_type.py new file mode 100644 index 00000000..c7796a48 --- /dev/null +++ b/src/intercom/types/ticket_reply_part_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketReplyPartType = typing.Union[typing.Literal["note", "comment", "quick_reply"], typing.Any] diff --git a/src/intercom/types/ticket_request_custom_attributes.py b/src/intercom/types/ticket_request_custom_attributes.py new file mode 100644 index 00000000..f17d452e --- /dev/null +++ b/src/intercom/types/ticket_request_custom_attributes.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketRequestCustomAttributes = typing.Dict[str, typing.Any] diff --git a/src/intercom/types/ticket_state_list.py b/src/intercom/types/ticket_state_list.py new file mode 100644 index 00000000..b70ef5ad --- /dev/null +++ b/src/intercom/types/ticket_state_list.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from ..tickets.types.ticket_state_detailed import TicketStateDetailed + + +class TicketStateList(UncheckedBaseModel): + """ + A list of ticket states associated with a given ticket type. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `list`. + """ + + data: typing.Optional[typing.List[typing.Optional[TicketStateDetailed]]] = pydantic.Field(default=None) + """ + A list of ticket states associated with a given ticket type. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/ticket_type_attribute.py b/src/intercom/types/ticket_type_attribute.py new file mode 100644 index 00000000..c6146148 --- /dev/null +++ b/src/intercom/types/ticket_type_attribute.py @@ -0,0 +1,108 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .ticket_type_attribute_data_type import TicketTypeAttributeDataType + + +class TicketTypeAttribute(UncheckedBaseModel): + """ + Ticket type attribute, used to define each data field to be captured in a ticket. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `ticket_type_attribute`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the ticket type attribute. + """ + + workspace_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the workspace that the ticket type attribute belongs to. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the ticket type attribute + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + The description of the ticket type attribute + """ + + data_type: typing.Optional[TicketTypeAttributeDataType] = pydantic.Field(default=None) + """ + The type of the data attribute (allowed values: "string list integer decimal boolean datetime files") + """ + + input_options: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + Input options for the attribute + """ + + order: typing.Optional[int] = pydantic.Field(default=None) + """ + The order of the attribute against other attributes + """ + + required_to_create: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the attribute is required or not for teammates. + """ + + required_to_create_for_contacts: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the attribute is required or not for contacts. + """ + + visible_on_create: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the attribute is visible or not to teammates. + """ + + visible_to_contacts: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the attribute is visible or not to contacts. + """ + + default: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the attribute is built in or not. + """ + + ticket_type_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of the ticket type that the attribute belongs to. + """ + + archived: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the ticket type attribute is archived or not. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The date and time the ticket type attribute was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The date and time the ticket type attribute was last updated. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/ticket_type_attribute_data_type.py b/src/intercom/types/ticket_type_attribute_data_type.py new file mode 100644 index 00000000..be33a888 --- /dev/null +++ b/src/intercom/types/ticket_type_attribute_data_type.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketTypeAttributeDataType = typing.Union[ + typing.Literal["string", "list", "integer", "decimal", "boolean", "datetime", "files"], typing.Any +] diff --git a/src/intercom/types/ticket_type_attribute_list.py b/src/intercom/types/ticket_type_attribute_list.py new file mode 100644 index 00000000..b74b43b5 --- /dev/null +++ b/src/intercom/types/ticket_type_attribute_list.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .ticket_type_attribute import TicketTypeAttribute + + +class TicketTypeAttributeList(UncheckedBaseModel): + """ + A list of attributes associated with a given ticket type. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `ticket_type_attributes.list`. + """ + + ticket_type_attributes: typing.Optional[typing.List[typing.Optional[TicketTypeAttribute]]] = pydantic.Field( + default=None + ) + """ + A list of ticket type attributes associated with a given ticket type. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/ticket_type_list.py b/src/intercom/types/ticket_type_list.py new file mode 100644 index 00000000..69711497 --- /dev/null +++ b/src/intercom/types/ticket_type_list.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from ..tickets.types.ticket_type import TicketType + + +class TicketTypeList(UncheckedBaseModel): + """ + A list of ticket types associated with a given workspace. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `list`. + """ + + data: typing.Optional[typing.List[typing.Optional[TicketType]]] = pydantic.Field(default=None) + """ + A list of ticket_types associated with a given workspace. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/translation.py b/src/intercom/types/translation.py new file mode 100644 index 00000000..9b108336 --- /dev/null +++ b/src/intercom/types/translation.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class Translation(UncheckedBaseModel): + """ + A translation object contains the localised details of a subscription type. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The localised name of the subscription type. + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + The localised description of the subscription type. + """ + + locale: typing.Optional[str] = pydantic.Field(default=None) + """ + The two character identifier for the language of the translation object. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/untag_company_request.py b/src/intercom/types/untag_company_request.py new file mode 100644 index 00000000..477bb3b3 --- /dev/null +++ b/src/intercom/types/untag_company_request.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .untag_company_request_companies_item import UntagCompanyRequestCompaniesItem + + +class UntagCompanyRequest(UncheckedBaseModel): + """ + You can tag a single company or a list of companies. + """ + + name: str = pydantic.Field() + """ + The name of the tag which will be untagged from the company + """ + + companies: typing.List[UntagCompanyRequestCompaniesItem] = pydantic.Field() + """ + The id or company_id of the company can be passed as input parameters. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/untag_company_request_companies_item.py b/src/intercom/types/untag_company_request_companies_item.py new file mode 100644 index 00000000..b87a55f3 --- /dev/null +++ b/src/intercom/types/untag_company_request_companies_item.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class UntagCompanyRequestCompaniesItem(UncheckedBaseModel): + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom defined id representing the company. + """ + + company_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The company id you have defined for the company. + """ + + untag: typing.Optional[bool] = pydantic.Field(default=None) + """ + Always set to true + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/update_article_request_body.py b/src/intercom/types/update_article_request_body.py new file mode 100644 index 00000000..58fb4364 --- /dev/null +++ b/src/intercom/types/update_article_request_body.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .update_article_request_body_parent_type import UpdateArticleRequestBodyParentType + + +class UpdateArticleRequestBody(UncheckedBaseModel): + parent_type: typing.Optional[UpdateArticleRequestBodyParentType] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/update_article_request_body_parent_type.py b/src/intercom/types/update_article_request_body_parent_type.py new file mode 100644 index 00000000..e7ce0e19 --- /dev/null +++ b/src/intercom/types/update_article_request_body_parent_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +UpdateArticleRequestBodyParentType = typing.Union[typing.Literal["collection", "section"], typing.Any] diff --git a/src/intercom/types/update_company_request_body.py b/src/intercom/types/update_company_request_body.py new file mode 100644 index 00000000..c4bff9e9 --- /dev/null +++ b/src/intercom/types/update_company_request_body.py @@ -0,0 +1,57 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class UpdateCompanyRequestBody(UncheckedBaseModel): + """ + You can update a Company + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the Company + """ + + plan: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the plan you have associated with the company. + """ + + size: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of employees in this company. + """ + + website: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL for this company's website. Please note that the value specified here is not validated. Accepts any string. + """ + + industry: typing.Optional[str] = pydantic.Field(default=None) + """ + The industry that this company operates in. + """ + + custom_attributes: typing.Optional[typing.Dict[str, str]] = pydantic.Field(default=None) + """ + A hash of key/value pairs containing any other data about the company you want Intercom to store. + """ + + monthly_spend: typing.Optional[int] = pydantic.Field(default=None) + """ + How much revenue the company generates for your business. Note that this will truncate floats. i.e. it only allow for whole integers, 155.98 will be truncated to 155. Note that this has an upper limit of 2**31-1 or 2147483647.. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/update_data_attribute_request_body.py b/src/intercom/types/update_data_attribute_request_body.py new file mode 100644 index 00000000..c7b69b7c --- /dev/null +++ b/src/intercom/types/update_data_attribute_request_body.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .update_data_attribute_request_options import UpdateDataAttributeRequestOptions + +UpdateDataAttributeRequestBody = typing.Union[UpdateDataAttributeRequestOptions, typing.Any] diff --git a/src/intercom/types/update_data_attribute_request_options.py b/src/intercom/types/update_data_attribute_request_options.py new file mode 100644 index 00000000..1fd40c3a --- /dev/null +++ b/src/intercom/types/update_data_attribute_request_options.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .update_data_attribute_request_options_options_item import UpdateDataAttributeRequestOptionsOptionsItem + + +class UpdateDataAttributeRequestOptions(UncheckedBaseModel): + options: typing.List[UpdateDataAttributeRequestOptionsOptionsItem] = pydantic.Field() + """ + Array of objects representing the options of the list, with `value` as the key and the option as the value. At least two options are required. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/update_data_attribute_request_options_options_item.py b/src/intercom/types/update_data_attribute_request_options_options_item.py new file mode 100644 index 00000000..42bce4e6 --- /dev/null +++ b/src/intercom/types/update_data_attribute_request_options_options_item.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class UpdateDataAttributeRequestOptionsOptionsItem(UncheckedBaseModel): + value: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/update_visitor_request.py b/src/intercom/types/update_visitor_request.py new file mode 100644 index 00000000..5f9c6890 --- /dev/null +++ b/src/intercom/types/update_visitor_request.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .update_visitor_request_with_id import UpdateVisitorRequestWithId +from .update_visitor_request_with_user_id import UpdateVisitorRequestWithUserId + +UpdateVisitorRequest = typing.Union[UpdateVisitorRequestWithId, UpdateVisitorRequestWithUserId] diff --git a/src/intercom/types/update_visitor_request_one.py b/src/intercom/types/update_visitor_request_one.py new file mode 100644 index 00000000..9b808e14 --- /dev/null +++ b/src/intercom/types/update_visitor_request_one.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +UpdateVisitorRequestOne = typing.Any diff --git a/src/intercom/types/update_visitor_request_with_id.py b/src/intercom/types/update_visitor_request_with_id.py new file mode 100644 index 00000000..a3ccacb3 --- /dev/null +++ b/src/intercom/types/update_visitor_request_with_id.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class UpdateVisitorRequestWithId(UncheckedBaseModel): + id: str = pydantic.Field() + """ + A unique identified for the visitor which is given by Intercom. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The visitor's name. + """ + + custom_attributes: typing.Optional[typing.Dict[str, str]] = pydantic.Field(default=None) + """ + The custom attributes which are set for the visitor. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/update_visitor_request_with_user_id.py b/src/intercom/types/update_visitor_request_with_user_id.py new file mode 100644 index 00000000..6d426bb2 --- /dev/null +++ b/src/intercom/types/update_visitor_request_with_user_id.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class UpdateVisitorRequestWithUserId(UncheckedBaseModel): + user_id: str = pydantic.Field() + """ + A unique identified for the visitor which is given by you. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The visitor's name. + """ + + custom_attributes: typing.Optional[typing.Dict[str, str]] = pydantic.Field(default=None) + """ + The custom attributes which are set for the visitor. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/visitor.py b/src/intercom/types/visitor.py new file mode 100644 index 00000000..e218e638 --- /dev/null +++ b/src/intercom/types/visitor.py @@ -0,0 +1,169 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .visitor_avatar import VisitorAvatar +from .visitor_companies import VisitorCompanies +from .visitor_location_data import VisitorLocationData +from .visitor_segments import VisitorSegments +from .visitor_social_profiles import VisitorSocialProfiles +from .visitor_tags import VisitorTags + + +class Visitor(UncheckedBaseModel): + """ + Visitors are useful for representing anonymous people that have not yet been identified. They usually represent website visitors. Visitors are not visible in Intercom platform. The Visitors resource provides methods to fetch, update, convert and delete. + """ + + type: typing.Optional[typing.Literal["visitor"]] = pydantic.Field(default=None) + """ + Value is 'visitor' + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom defined id representing the Visitor. + """ + + user_id: typing.Optional[str] = pydantic.Field(default=None) + """ + Automatically generated identifier for the Visitor. + """ + + anonymous: typing.Optional[bool] = pydantic.Field(default=None) + """ + Identifies if this visitor is anonymous. + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + The email of the visitor. + """ + + phone: typing.Optional[str] = pydantic.Field(default=None) + """ + The phone number of the visitor. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the visitor. + """ + + pseudonym: typing.Optional[str] = pydantic.Field(default=None) + """ + The pseudonym of the visitor. + """ + + avatar: typing.Optional[VisitorAvatar] = None + app_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the app the visitor is associated with. + """ + + companies: typing.Optional[VisitorCompanies] = None + location_data: typing.Optional[VisitorLocationData] = None + las_request_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the Lead last recorded making a request. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the Visitor was added to Intercom. + """ + + remote_created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the Visitor was added to Intercom. + """ + + signed_up_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the Visitor signed up for your product. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The last time the Visitor was updated. + """ + + session_count: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of sessions the Visitor has had. + """ + + social_profiles: typing.Optional[VisitorSocialProfiles] = None + owner_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the admin that owns the Visitor. + """ + + unsubscribed_from_emails: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the Visitor is unsubscribed from emails. + """ + + marked_email_as_spam: typing.Optional[bool] = pydantic.Field(default=None) + """ + Identifies if this visitor has marked an email as spam. + """ + + has_hard_bounced: typing.Optional[bool] = pydantic.Field(default=None) + """ + Identifies if this visitor has had a hard bounce. + """ + + tags: typing.Optional[VisitorTags] = None + segments: typing.Optional[VisitorSegments] = None + custom_attributes: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + The custom attributes you have set on the Visitor. + """ + + referrer: typing.Optional[str] = pydantic.Field(default=None) + """ + The referer of the visitor. + """ + + utm_campaign: typing.Optional[str] = pydantic.Field(default=None) + """ + The utm_campaign of the visitor. + """ + + utm_content: typing.Optional[str] = pydantic.Field(default=None) + """ + The utm_content of the visitor. + """ + + utm_medium: typing.Optional[str] = pydantic.Field(default=None) + """ + The utm_medium of the visitor. + """ + + utm_source: typing.Optional[str] = pydantic.Field(default=None) + """ + The utm_source of the visitor. + """ + + utm_term: typing.Optional[str] = pydantic.Field(default=None) + """ + The utm_term of the visitor. + """ + + do_not_track: typing.Optional[bool] = pydantic.Field(default=None) + """ + Identifies if this visitor has do not track enabled. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/visitor_avatar.py b/src/intercom/types/visitor_avatar.py new file mode 100644 index 00000000..9a30724f --- /dev/null +++ b/src/intercom/types/visitor_avatar.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class VisitorAvatar(UncheckedBaseModel): + type: typing.Optional[str] = pydantic.Field(default=None) + """ + + """ + + image_url: typing.Optional[str] = pydantic.Field(default=None) + """ + This object represents the avatar associated with the visitor. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/visitor_companies.py b/src/intercom/types/visitor_companies.py new file mode 100644 index 00000000..c35fc0e3 --- /dev/null +++ b/src/intercom/types/visitor_companies.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..companies.types.company import Company +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class VisitorCompanies(UncheckedBaseModel): + type: typing.Optional[typing.Literal["company.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + companies: typing.Optional[typing.List[Company]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/visitor_deleted_object.py b/src/intercom/types/visitor_deleted_object.py new file mode 100644 index 00000000..1246b348 --- /dev/null +++ b/src/intercom/types/visitor_deleted_object.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class VisitorDeletedObject(UncheckedBaseModel): + """ + Response returned when an object is deleted + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the visitor which is given by Intercom. + """ + + type: typing.Optional[typing.Literal["visitor"]] = pydantic.Field(default=None) + """ + The type of object which was deleted + """ + + user_id: typing.Optional[str] = pydantic.Field(default=None) + """ + Automatically generated identifier for the Visitor. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/visitor_location_data.py b/src/intercom/types/visitor_location_data.py new file mode 100644 index 00000000..400d6373 --- /dev/null +++ b/src/intercom/types/visitor_location_data.py @@ -0,0 +1,58 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class VisitorLocationData(UncheckedBaseModel): + type: typing.Optional[str] = pydantic.Field(default=None) + """ + + """ + + city_name: typing.Optional[str] = pydantic.Field(default=None) + """ + The city name of the visitor. + """ + + continent_code: typing.Optional[str] = pydantic.Field(default=None) + """ + The continent code of the visitor. + """ + + country_code: typing.Optional[str] = pydantic.Field(default=None) + """ + The country code of the visitor. + """ + + country_name: typing.Optional[str] = pydantic.Field(default=None) + """ + The country name of the visitor. + """ + + postal_code: typing.Optional[str] = pydantic.Field(default=None) + """ + The postal code of the visitor. + """ + + region_name: typing.Optional[str] = pydantic.Field(default=None) + """ + The region name of the visitor. + """ + + timezone: typing.Optional[str] = pydantic.Field(default=None) + """ + The timezone of the visitor. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/visitor_segments.py b/src/intercom/types/visitor_segments.py new file mode 100644 index 00000000..80d23100 --- /dev/null +++ b/src/intercom/types/visitor_segments.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class VisitorSegments(UncheckedBaseModel): + type: typing.Optional[typing.Literal["segment.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + segments: typing.Optional[typing.List[str]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/visitor_social_profiles.py b/src/intercom/types/visitor_social_profiles.py new file mode 100644 index 00000000..008fbe15 --- /dev/null +++ b/src/intercom/types/visitor_social_profiles.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class VisitorSocialProfiles(UncheckedBaseModel): + type: typing.Optional[typing.Literal["social_profile.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + social_profiles: typing.Optional[typing.List[str]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/visitor_tags.py b/src/intercom/types/visitor_tags.py new file mode 100644 index 00000000..e3119139 --- /dev/null +++ b/src/intercom/types/visitor_tags.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .visitor_tags_tags_item import VisitorTagsTagsItem + + +class VisitorTags(UncheckedBaseModel): + type: typing.Optional[typing.Literal["tag.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + tags: typing.Optional[typing.List[VisitorTagsTagsItem]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/visitor_tags_tags_item.py b/src/intercom/types/visitor_tags_tags_item.py new file mode 100644 index 00000000..e0ae6591 --- /dev/null +++ b/src/intercom/types/visitor_tags_tags_item.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class VisitorTagsTagsItem(UncheckedBaseModel): + type: typing.Optional[typing.Literal["tag"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the tag. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the tag. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/whatsapp_message_status_list.py b/src/intercom/types/whatsapp_message_status_list.py new file mode 100644 index 00000000..fcadc4b6 --- /dev/null +++ b/src/intercom/types/whatsapp_message_status_list.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .whatsapp_message_status_list_events_item import WhatsappMessageStatusListEventsItem +from .whatsapp_message_status_list_pages import WhatsappMessageStatusListPages + + +class WhatsappMessageStatusList(UncheckedBaseModel): + type: typing.Literal["list"] = "list" + ruleset_id: str = pydantic.Field() + """ + The provided ruleset ID + """ + + pages: WhatsappMessageStatusListPages + total_count: int = pydantic.Field() + """ + Total number of events + """ + + events: typing.List[WhatsappMessageStatusListEventsItem] + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/whatsapp_message_status_list_events_item.py b/src/intercom/types/whatsapp_message_status_list_events_item.py new file mode 100644 index 00000000..19ec9f8e --- /dev/null +++ b/src/intercom/types/whatsapp_message_status_list_events_item.py @@ -0,0 +1,59 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .whatsapp_message_status_list_events_item_status import WhatsappMessageStatusListEventsItemStatus + + +class WhatsappMessageStatusListEventsItem(UncheckedBaseModel): + id: str = pydantic.Field() + """ + Event ID + """ + + conversation_id: str = pydantic.Field() + """ + ID of the conversation + """ + + status: WhatsappMessageStatusListEventsItemStatus = pydantic.Field() + """ + Current status of the message + """ + + type: typing.Literal["broadcast_outbound"] = pydantic.Field(default="broadcast_outbound") + """ + Event type + """ + + created_at: int = pydantic.Field() + """ + Creation timestamp + """ + + updated_at: int = pydantic.Field() + """ + Last update timestamp + """ + + whatsapp_message_id: str = pydantic.Field() + """ + WhatsApp's message identifier + """ + + template_name: typing.Optional[str] = pydantic.Field(default=None) + """ + Name of the WhatsApp template used + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/whatsapp_message_status_list_events_item_status.py b/src/intercom/types/whatsapp_message_status_list_events_item_status.py new file mode 100644 index 00000000..3db079a1 --- /dev/null +++ b/src/intercom/types/whatsapp_message_status_list_events_item_status.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +WhatsappMessageStatusListEventsItemStatus = typing.Union[ + typing.Literal["sent", "delivered", "read", "failed"], typing.Any +] diff --git a/src/intercom/types/whatsapp_message_status_list_pages.py b/src/intercom/types/whatsapp_message_status_list_pages.py new file mode 100644 index 00000000..5af4f331 --- /dev/null +++ b/src/intercom/types/whatsapp_message_status_list_pages.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel +from .whatsapp_message_status_list_pages_next import WhatsappMessageStatusListPagesNext + + +class WhatsappMessageStatusListPages(UncheckedBaseModel): + type: typing.Literal["pages"] = "pages" + per_page: int = pydantic.Field() + """ + Number of results per page + """ + + total_pages: int = pydantic.Field() + """ + Total number of pages + """ + + next: typing.Optional[WhatsappMessageStatusListPagesNext] = pydantic.Field(default=None) + """ + Information for fetching next page (null if no more pages) + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/types/whatsapp_message_status_list_pages_next.py b/src/intercom/types/whatsapp_message_status_list_pages_next.py new file mode 100644 index 00000000..c9974e5d --- /dev/null +++ b/src/intercom/types/whatsapp_message_status_list_pages_next.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from ..core.unchecked_base_model import UncheckedBaseModel + + +class WhatsappMessageStatusListPagesNext(UncheckedBaseModel): + """ + Information for fetching next page (null if no more pages) + """ + + starting_after: typing.Optional[str] = pydantic.Field(default=None) + """ + Cursor for the next page + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/__init__.py b/src/intercom/unstable/__init__.py new file mode 100644 index 00000000..9b77f13f --- /dev/null +++ b/src/intercom/unstable/__init__.py @@ -0,0 +1,1255 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + ActivityLog, + ActivityLogActivityType, + ActivityLogList, + ActivityLogMetadata, + ActivityLogPerformedBy, + AddressableList, + AdminList, + AdminPriorityLevel, + AdminReplyConversationRequest, + AdminReplyConversationRequestMessageType, + AdminReplyTicketRequest, + AdminReplyTicketRequestMessageType, + AdminReplyTicketRequestReplyOptionsItem, + AdminWithApp, + AdminWithAppAvatar, + AiCallResponse, + App, + ArticleContent, + ArticleContentState, + ArticleList, + ArticleStatistics, + ArticleTranslatedContent, + AssignConversationRequest, + AssignConversationRequestType, + AwayStatusReason, + CallList, + CloseConversationRequest, + CollectionList, + CompanyAttachedContacts, + CompanyAttachedSegments, + CompanyData, + CompanyList, + CompanyScroll, + ContactArchived, + ContactAttachedCompanies, + ContactBlocked, + ContactCompanies, + ContactDeleted, + ContactList, + ContactLocation, + ContactNotes, + ContactReference, + ContactReplyBaseRequest, + ContactReplyBaseRequestReplyOptionsItem, + ContactReplyConversationRequest, + ContactReplyEmailRequest, + ContactReplyIntercomUserIdRequest, + ContactReplyTicketEmailRequest, + ContactReplyTicketIntercomUserIdRequest, + ContactReplyTicketRequest, + ContactReplyTicketUserIdRequest, + ContactReplyUserIdRequest, + ContactSegments, + ContactSocialProfiles, + ContactSubscriptionTypes, + ContactTags, + ContactUnarchived, + ContentSourcesList, + ConversationAttachmentFiles, + ConversationAttributeUpdatedByAdmin, + ConversationAttributeUpdatedByAdminAttribute, + ConversationAttributeUpdatedByAdminValue, + ConversationAttributeUpdatedByWorkflow, + ConversationAttributeUpdatedByWorkflowAttribute, + ConversationAttributeUpdatedByWorkflowValue, + ConversationAttributeUpdatedByWorkflowWorkflow, + ConversationContacts, + ConversationDeleted, + ConversationFirstContactReply, + ConversationList, + ConversationPart, + ConversationPartAuthor, + ConversationPartMetadata, + ConversationPartMetadataQuickReplyOptionsItem, + ConversationPartState, + ConversationParts, + ConversationRating, + ConversationResponseTime, + ConversationSource, + ConversationSourceType, + ConversationStatistics, + ConversationTeammates, + CreateArticleRequest, + CreateArticleRequestState, + CreateDataAttributeRequest, + CreateDataAttributeRequestOne, + CreateDataAttributeRequestOneDataType, + CreateDataAttributeRequestOptions, + CreateDataAttributeRequestOptionsOptionsItem, + CreateInternalArticleRequest, + CreateMessageRequest, + CreateOrUpdateCompanyRequest, + CreateOrUpdateTagRequest, + CreatePhoneSwitchRequest, + CreateTicketReplyWithCommentRequest, + CreateTicketRequestAssignment, + CreateTicketRequestBody, + CreateTicketRequestContactsItem, + CreateTicketRequestContactsItemEmail, + CreateTicketRequestContactsItemExternalId, + CreateTicketRequestContactsItemId, + CreateTicketTypeRequest, + CreateTicketTypeRequestCategory, + CursorPages, + CustomActionFinished, + CustomActionFinishedAction, + CustomActionFinishedActionResult, + CustomActionStarted, + CustomActionStartedAction, + CustomAttributes, + CustomAttributesValue, + CustomChannelAttribute, + CustomChannelBaseEvent, + CustomChannelContact, + CustomChannelContactType, + CustomChannelNotificationResponse, + CustomObjectInstanceDeleted, + CustomObjectInstanceList, + CustomerRequest, + CustomerRequestEmail, + CustomerRequestIntercomUserId, + CustomerRequestUserId, + DataAttributeList, + DataEventList, + DataEventListPages, + DataEventSummary, + DataEventSummaryItem, + DataExportCsv, + Datetime, + DeletedArticleObject, + DeletedCollectionObject, + DeletedCompanyObject, + DeletedInternalArticleObject, + DeletedObject, + EmailAddressHeader, + EmailMessageMetadata, + Error, + ErrorErrorsItem, + EventDetails, + FileAttribute, + GroupContent, + GroupTranslatedContent, + InternalArticleList, + LinkedObject, + LinkedObjectCategory, + LinkedObjectList, + LinkedObjectType, + MultipleFilterSearchRequest, + MultipleFilterSearchRequestOperator, + MultipleFilterSearchRequestValue, + NewsItemRequest, + NewsItemRequestState, + NotFoundErrorBody, + NotFoundErrorBodyErrorsItem, + NoteList, + OpenConversationRequest, + OperatorWorkflowEvent, + OperatorWorkflowEventEvent, + OperatorWorkflowEventWorkflow, + PagesLink, + PaginatedResponse, + PaginatedResponseDataItem, + PaginatedResponseDataItem_NewsItem, + PaginatedResponseDataItem_Newsfeed, + PaginatedResponseType, + PartAttachment, + PhoneSwitch, + QuickReplyOption, + Recipient, + RecipientType, + RedactConversationRequest, + RedactConversationRequestConversationPart, + RedactConversationRequestSource, + RedactConversationRequest_ConversationPart, + RedactConversationRequest_Source, + Reference, + RegisterFinVoiceCallRequest, + RegisterFinVoiceCallRequestSource, + ReplyConversationRequestBody, + SearchRequest, + SearchRequestQuery, + SegmentList, + SingleFilterSearchRequest, + SingleFilterSearchRequestOperator, + SingleFilterSearchRequestValue, + SingleFilterSearchRequestValueTwoItem, + SlaApplied, + SlaAppliedSlaStatus, + SnoozeConversationRequest, + SocialProfile, + StartingAfterPaging, + SubscriptionTypeList, + TagCompanyRequest, + TagCompanyRequestCompaniesItem, + TagList, + TagMultipleUsersRequest, + TagMultipleUsersRequestUsersItem, + Tags, + TeamList, + TeamPriorityLevel, + TicketCustomAttributes, + TicketCustomAttributesValue, + TicketList, + TicketPartAuthor, + TicketPartAuthorType, + TicketParts, + TicketReply, + TicketReplyPartType, + TicketRequestCustomAttributes, + TicketRequestCustomAttributesValue, + TicketStateList, + TicketTypeAttribute, + TicketTypeAttributeList, + TicketTypeList, + Translation, + UntagCompanyRequest, + UntagCompanyRequestCompaniesItem, + UpdateDataAttributeRequestBody, + UpdateDataAttributeRequestOptions, + UpdateDataAttributeRequestOptionsOptionsItem, + Visitor, + VisitorAvatar, + VisitorCompanies, + VisitorDeletedObject, + VisitorLocationData, + VisitorSegments, + VisitorSocialProfiles, + VisitorTags, + VisitorTagsTagsItem, + WhatsappMessageStatusList, + WhatsappMessageStatusListEventsItem, + WhatsappMessageStatusListEventsItemStatus, + WhatsappMessageStatusListPages, + WhatsappMessageStatusListPagesNext, + ) + from .errors import ( + BadRequestError, + ConflictError, + ForbiddenError, + InternalServerError, + NotFoundError, + TooManyRequestsError, + UnauthorizedError, + UnprocessableEntityError, + ) + from . import ( + admins, + ai_agent, + ai_content, + ai_content_source, + articles, + away_status_reasons, + brands, + calls, + companies, + contacts, + conversations, + custom_channel_events, + custom_object_instances, + data_attributes, + data_events, + data_export, + emails, + export, + help_center, + internal_articles, + jobs, + macros, + messages, + news, + notes, + segments, + subscription_types, + switch, + tags, + teams, + ticket_states, + ticket_type_attributes, + ticket_types, + tickets, + visitors, + ) + from .admins import Admin + from .ai_agent import AiAgent, AiAgentLastAnswerType, AiAgentResolutionState, AiAgentSourceType + from .ai_content import ( + ContentImportSource, + ContentImportSourceStatus, + ContentImportSourceSyncBehavior, + ContentImportSourcesList, + CreateContentImportSourceRequestStatus, + ExternalPage, + ExternalPagesList, + UpdateContentImportSourceRequestStatus, + UpdateContentImportSourceRequestSyncBehavior, + ) + from .ai_content_source import ContentSource, ContentSourceContentType + from .articles import ( + Article, + ArticleListItem, + ArticleListItemState, + ArticleSearchHighlights, + ArticleSearchHighlightsHighlightedSummaryItemItem, + ArticleSearchHighlightsHighlightedSummaryItemItemType, + ArticleSearchHighlightsHighlightedTitleItem, + ArticleSearchHighlightsHighlightedTitleItemType, + ArticleSearchResponse, + ArticleSearchResponseData, + InternalArticle, + ) + from .brands import Brand, BrandList + from .calls import Call, ListCallsWithTranscriptsResponse, ListCallsWithTranscriptsResponseDataItem + from .companies import Company, CompanyNotes, CompanyPlan, CompanySegments, CompanyTags + from .contacts import ( + Contact, + ContactAvatar, + CreateContactResponse, + MergeContactResponse, + ShowContactByExternalIdResponse, + ShowContactResponse, + UpdateContactResponse, + ) + from .conversations import ( + AttachContactToConversationRequestCustomer, + AttachContactToConversationRequestCustomerCustomer, + AttachContactToConversationRequestCustomerIntercomUserId, + AttachContactToConversationRequestCustomerUserId, + Conversation, + ConversationPriority, + ConversationState, + CreateConversationRequestFrom, + CreateConversationRequestFromType, + ManageConversationRequestBody, + ManageConversationRequestBody_Assignment, + ManageConversationRequestBody_Close, + ManageConversationRequestBody_Open, + ManageConversationRequestBody_Snoozed, + ) + from .custom_object_instances import CustomObjectInstance + from .data_attributes import DataAttribute, DataAttributeDataType, DataAttributeModel, LisDataAttributesRequestModel + from .data_events import ( + CreateDataEventSummariesRequestEventSummaries, + DataEvent, + LisDataEventsRequestFilter, + LisDataEventsRequestFilterEmail, + LisDataEventsRequestFilterIntercomUserId, + LisDataEventsRequestFilterUserId, + ) + from .data_export import DataExport, DataExportStatus + from .emails import EmailList, EmailSetting + from .export import ( + GetExportReportingDataGetDatasetsResponse, + GetExportReportingDataGetDatasetsResponseDataItem, + GetExportReportingDataGetDatasetsResponseDataItemAttributesItem, + PostExportReportingDataEnqueueResponse, + ) + from .help_center import Collection, HelpCenter, HelpCenterList + from .internal_articles import ( + InternalArticleListItem, + InternalArticleSearchResponse, + InternalArticleSearchResponseData, + ) + from .jobs import Jobs, JobsStatus + from .macros import Macro, MacroAvailableOnItem, MacroList, MacroListPages, MacroListPagesNext, MacroVisibleTo + from .messages import Message, MessageMessageType + from .news import NewsItem, NewsItemState, Newsfeed, NewsfeedAssignment + from .notes import CompanyNote, CompanyNoteCompany, Note, NoteContact + from .segments import Segment, SegmentPersonType + from .subscription_types import ( + SubscriptionType, + SubscriptionTypeConsentType, + SubscriptionTypeContentTypesItem, + SubscriptionTypeState, + ) + from .tags import CreateTagRequestBody, Tag, TagBasic + from .teams import Team + from .ticket_type_attributes import CreateTicketTypeAttributeRequestDataType + from .tickets import ( + DeleteTicketResponse, + ReplyTicketRequestBody, + Ticket, + TicketCategory, + TicketContacts, + TicketPart, + TicketPartPreviousTicketState, + TicketPartTicketState, + TicketPartUpdatedAttributeData, + TicketPartUpdatedAttributeDataAttribute, + TicketPartUpdatedAttributeDataValue, + TicketPartUpdatedAttributeDataValueId, + TicketPartUpdatedAttributeDataValueLabel, + TicketState, + TicketStateCategory, + TicketStateDetailed, + TicketStateDetailedCategory, + TicketStateDetailedTicketTypes, + TicketType, + TicketTypeCategory, + TicketTypeTicketStates, + ) +_dynamic_imports: typing.Dict[str, str] = { + "ActivityLog": ".types", + "ActivityLogActivityType": ".types", + "ActivityLogList": ".types", + "ActivityLogMetadata": ".types", + "ActivityLogPerformedBy": ".types", + "AddressableList": ".types", + "Admin": ".admins", + "AdminList": ".types", + "AdminPriorityLevel": ".types", + "AdminReplyConversationRequest": ".types", + "AdminReplyConversationRequestMessageType": ".types", + "AdminReplyTicketRequest": ".types", + "AdminReplyTicketRequestMessageType": ".types", + "AdminReplyTicketRequestReplyOptionsItem": ".types", + "AdminWithApp": ".types", + "AdminWithAppAvatar": ".types", + "AiAgent": ".ai_agent", + "AiAgentLastAnswerType": ".ai_agent", + "AiAgentResolutionState": ".ai_agent", + "AiAgentSourceType": ".ai_agent", + "AiCallResponse": ".types", + "App": ".types", + "Article": ".articles", + "ArticleContent": ".types", + "ArticleContentState": ".types", + "ArticleList": ".types", + "ArticleListItem": ".articles", + "ArticleListItemState": ".articles", + "ArticleSearchHighlights": ".articles", + "ArticleSearchHighlightsHighlightedSummaryItemItem": ".articles", + "ArticleSearchHighlightsHighlightedSummaryItemItemType": ".articles", + "ArticleSearchHighlightsHighlightedTitleItem": ".articles", + "ArticleSearchHighlightsHighlightedTitleItemType": ".articles", + "ArticleSearchResponse": ".articles", + "ArticleSearchResponseData": ".articles", + "ArticleStatistics": ".types", + "ArticleTranslatedContent": ".types", + "AssignConversationRequest": ".types", + "AssignConversationRequestType": ".types", + "AttachContactToConversationRequestCustomer": ".conversations", + "AttachContactToConversationRequestCustomerCustomer": ".conversations", + "AttachContactToConversationRequestCustomerIntercomUserId": ".conversations", + "AttachContactToConversationRequestCustomerUserId": ".conversations", + "AwayStatusReason": ".types", + "BadRequestError": ".errors", + "Brand": ".brands", + "BrandList": ".brands", + "Call": ".calls", + "CallList": ".types", + "CloseConversationRequest": ".types", + "Collection": ".help_center", + "CollectionList": ".types", + "Company": ".companies", + "CompanyAttachedContacts": ".types", + "CompanyAttachedSegments": ".types", + "CompanyData": ".types", + "CompanyList": ".types", + "CompanyNote": ".notes", + "CompanyNoteCompany": ".notes", + "CompanyNotes": ".companies", + "CompanyPlan": ".companies", + "CompanyScroll": ".types", + "CompanySegments": ".companies", + "CompanyTags": ".companies", + "ConflictError": ".errors", + "Contact": ".contacts", + "ContactArchived": ".types", + "ContactAttachedCompanies": ".types", + "ContactAvatar": ".contacts", + "ContactBlocked": ".types", + "ContactCompanies": ".types", + "ContactDeleted": ".types", + "ContactList": ".types", + "ContactLocation": ".types", + "ContactNotes": ".types", + "ContactReference": ".types", + "ContactReplyBaseRequest": ".types", + "ContactReplyBaseRequestReplyOptionsItem": ".types", + "ContactReplyConversationRequest": ".types", + "ContactReplyEmailRequest": ".types", + "ContactReplyIntercomUserIdRequest": ".types", + "ContactReplyTicketEmailRequest": ".types", + "ContactReplyTicketIntercomUserIdRequest": ".types", + "ContactReplyTicketRequest": ".types", + "ContactReplyTicketUserIdRequest": ".types", + "ContactReplyUserIdRequest": ".types", + "ContactSegments": ".types", + "ContactSocialProfiles": ".types", + "ContactSubscriptionTypes": ".types", + "ContactTags": ".types", + "ContactUnarchived": ".types", + "ContentImportSource": ".ai_content", + "ContentImportSourceStatus": ".ai_content", + "ContentImportSourceSyncBehavior": ".ai_content", + "ContentImportSourcesList": ".ai_content", + "ContentSource": ".ai_content_source", + "ContentSourceContentType": ".ai_content_source", + "ContentSourcesList": ".types", + "Conversation": ".conversations", + "ConversationAttachmentFiles": ".types", + "ConversationAttributeUpdatedByAdmin": ".types", + "ConversationAttributeUpdatedByAdminAttribute": ".types", + "ConversationAttributeUpdatedByAdminValue": ".types", + "ConversationAttributeUpdatedByWorkflow": ".types", + "ConversationAttributeUpdatedByWorkflowAttribute": ".types", + "ConversationAttributeUpdatedByWorkflowValue": ".types", + "ConversationAttributeUpdatedByWorkflowWorkflow": ".types", + "ConversationContacts": ".types", + "ConversationDeleted": ".types", + "ConversationFirstContactReply": ".types", + "ConversationList": ".types", + "ConversationPart": ".types", + "ConversationPartAuthor": ".types", + "ConversationPartMetadata": ".types", + "ConversationPartMetadataQuickReplyOptionsItem": ".types", + "ConversationPartState": ".types", + "ConversationParts": ".types", + "ConversationPriority": ".conversations", + "ConversationRating": ".types", + "ConversationResponseTime": ".types", + "ConversationSource": ".types", + "ConversationSourceType": ".types", + "ConversationState": ".conversations", + "ConversationStatistics": ".types", + "ConversationTeammates": ".types", + "CreateArticleRequest": ".types", + "CreateArticleRequestState": ".types", + "CreateContactResponse": ".contacts", + "CreateContentImportSourceRequestStatus": ".ai_content", + "CreateConversationRequestFrom": ".conversations", + "CreateConversationRequestFromType": ".conversations", + "CreateDataAttributeRequest": ".types", + "CreateDataAttributeRequestOne": ".types", + "CreateDataAttributeRequestOneDataType": ".types", + "CreateDataAttributeRequestOptions": ".types", + "CreateDataAttributeRequestOptionsOptionsItem": ".types", + "CreateDataEventSummariesRequestEventSummaries": ".data_events", + "CreateInternalArticleRequest": ".types", + "CreateMessageRequest": ".types", + "CreateOrUpdateCompanyRequest": ".types", + "CreateOrUpdateTagRequest": ".types", + "CreatePhoneSwitchRequest": ".types", + "CreateTagRequestBody": ".tags", + "CreateTicketReplyWithCommentRequest": ".types", + "CreateTicketRequestAssignment": ".types", + "CreateTicketRequestBody": ".types", + "CreateTicketRequestContactsItem": ".types", + "CreateTicketRequestContactsItemEmail": ".types", + "CreateTicketRequestContactsItemExternalId": ".types", + "CreateTicketRequestContactsItemId": ".types", + "CreateTicketTypeAttributeRequestDataType": ".ticket_type_attributes", + "CreateTicketTypeRequest": ".types", + "CreateTicketTypeRequestCategory": ".types", + "CursorPages": ".types", + "CustomActionFinished": ".types", + "CustomActionFinishedAction": ".types", + "CustomActionFinishedActionResult": ".types", + "CustomActionStarted": ".types", + "CustomActionStartedAction": ".types", + "CustomAttributes": ".types", + "CustomAttributesValue": ".types", + "CustomChannelAttribute": ".types", + "CustomChannelBaseEvent": ".types", + "CustomChannelContact": ".types", + "CustomChannelContactType": ".types", + "CustomChannelNotificationResponse": ".types", + "CustomObjectInstance": ".custom_object_instances", + "CustomObjectInstanceDeleted": ".types", + "CustomObjectInstanceList": ".types", + "CustomerRequest": ".types", + "CustomerRequestEmail": ".types", + "CustomerRequestIntercomUserId": ".types", + "CustomerRequestUserId": ".types", + "DataAttribute": ".data_attributes", + "DataAttributeDataType": ".data_attributes", + "DataAttributeList": ".types", + "DataAttributeModel": ".data_attributes", + "DataEvent": ".data_events", + "DataEventList": ".types", + "DataEventListPages": ".types", + "DataEventSummary": ".types", + "DataEventSummaryItem": ".types", + "DataExport": ".data_export", + "DataExportCsv": ".types", + "DataExportStatus": ".data_export", + "Datetime": ".types", + "DeleteTicketResponse": ".tickets", + "DeletedArticleObject": ".types", + "DeletedCollectionObject": ".types", + "DeletedCompanyObject": ".types", + "DeletedInternalArticleObject": ".types", + "DeletedObject": ".types", + "EmailAddressHeader": ".types", + "EmailList": ".emails", + "EmailMessageMetadata": ".types", + "EmailSetting": ".emails", + "Error": ".types", + "ErrorErrorsItem": ".types", + "EventDetails": ".types", + "ExternalPage": ".ai_content", + "ExternalPagesList": ".ai_content", + "FileAttribute": ".types", + "ForbiddenError": ".errors", + "GetExportReportingDataGetDatasetsResponse": ".export", + "GetExportReportingDataGetDatasetsResponseDataItem": ".export", + "GetExportReportingDataGetDatasetsResponseDataItemAttributesItem": ".export", + "GroupContent": ".types", + "GroupTranslatedContent": ".types", + "HelpCenter": ".help_center", + "HelpCenterList": ".help_center", + "InternalArticle": ".articles", + "InternalArticleList": ".types", + "InternalArticleListItem": ".internal_articles", + "InternalArticleSearchResponse": ".internal_articles", + "InternalArticleSearchResponseData": ".internal_articles", + "InternalServerError": ".errors", + "Jobs": ".jobs", + "JobsStatus": ".jobs", + "LinkedObject": ".types", + "LinkedObjectCategory": ".types", + "LinkedObjectList": ".types", + "LinkedObjectType": ".types", + "LisDataAttributesRequestModel": ".data_attributes", + "LisDataEventsRequestFilter": ".data_events", + "LisDataEventsRequestFilterEmail": ".data_events", + "LisDataEventsRequestFilterIntercomUserId": ".data_events", + "LisDataEventsRequestFilterUserId": ".data_events", + "ListCallsWithTranscriptsResponse": ".calls", + "ListCallsWithTranscriptsResponseDataItem": ".calls", + "Macro": ".macros", + "MacroAvailableOnItem": ".macros", + "MacroList": ".macros", + "MacroListPages": ".macros", + "MacroListPagesNext": ".macros", + "MacroVisibleTo": ".macros", + "ManageConversationRequestBody": ".conversations", + "ManageConversationRequestBody_Assignment": ".conversations", + "ManageConversationRequestBody_Close": ".conversations", + "ManageConversationRequestBody_Open": ".conversations", + "ManageConversationRequestBody_Snoozed": ".conversations", + "MergeContactResponse": ".contacts", + "Message": ".messages", + "MessageMessageType": ".messages", + "MultipleFilterSearchRequest": ".types", + "MultipleFilterSearchRequestOperator": ".types", + "MultipleFilterSearchRequestValue": ".types", + "NewsItem": ".news", + "NewsItemRequest": ".types", + "NewsItemRequestState": ".types", + "NewsItemState": ".news", + "Newsfeed": ".news", + "NewsfeedAssignment": ".news", + "NotFoundError": ".errors", + "NotFoundErrorBody": ".types", + "NotFoundErrorBodyErrorsItem": ".types", + "Note": ".notes", + "NoteContact": ".notes", + "NoteList": ".types", + "OpenConversationRequest": ".types", + "OperatorWorkflowEvent": ".types", + "OperatorWorkflowEventEvent": ".types", + "OperatorWorkflowEventWorkflow": ".types", + "PagesLink": ".types", + "PaginatedResponse": ".types", + "PaginatedResponseDataItem": ".types", + "PaginatedResponseDataItem_NewsItem": ".types", + "PaginatedResponseDataItem_Newsfeed": ".types", + "PaginatedResponseType": ".types", + "PartAttachment": ".types", + "PhoneSwitch": ".types", + "PostExportReportingDataEnqueueResponse": ".export", + "QuickReplyOption": ".types", + "Recipient": ".types", + "RecipientType": ".types", + "RedactConversationRequest": ".types", + "RedactConversationRequestConversationPart": ".types", + "RedactConversationRequestSource": ".types", + "RedactConversationRequest_ConversationPart": ".types", + "RedactConversationRequest_Source": ".types", + "Reference": ".types", + "RegisterFinVoiceCallRequest": ".types", + "RegisterFinVoiceCallRequestSource": ".types", + "ReplyConversationRequestBody": ".types", + "ReplyTicketRequestBody": ".tickets", + "SearchRequest": ".types", + "SearchRequestQuery": ".types", + "Segment": ".segments", + "SegmentList": ".types", + "SegmentPersonType": ".segments", + "ShowContactByExternalIdResponse": ".contacts", + "ShowContactResponse": ".contacts", + "SingleFilterSearchRequest": ".types", + "SingleFilterSearchRequestOperator": ".types", + "SingleFilterSearchRequestValue": ".types", + "SingleFilterSearchRequestValueTwoItem": ".types", + "SlaApplied": ".types", + "SlaAppliedSlaStatus": ".types", + "SnoozeConversationRequest": ".types", + "SocialProfile": ".types", + "StartingAfterPaging": ".types", + "SubscriptionType": ".subscription_types", + "SubscriptionTypeConsentType": ".subscription_types", + "SubscriptionTypeContentTypesItem": ".subscription_types", + "SubscriptionTypeList": ".types", + "SubscriptionTypeState": ".subscription_types", + "Tag": ".tags", + "TagBasic": ".tags", + "TagCompanyRequest": ".types", + "TagCompanyRequestCompaniesItem": ".types", + "TagList": ".types", + "TagMultipleUsersRequest": ".types", + "TagMultipleUsersRequestUsersItem": ".types", + "Tags": ".types", + "Team": ".teams", + "TeamList": ".types", + "TeamPriorityLevel": ".types", + "Ticket": ".tickets", + "TicketCategory": ".tickets", + "TicketContacts": ".tickets", + "TicketCustomAttributes": ".types", + "TicketCustomAttributesValue": ".types", + "TicketList": ".types", + "TicketPart": ".tickets", + "TicketPartAuthor": ".types", + "TicketPartAuthorType": ".types", + "TicketPartPreviousTicketState": ".tickets", + "TicketPartTicketState": ".tickets", + "TicketPartUpdatedAttributeData": ".tickets", + "TicketPartUpdatedAttributeDataAttribute": ".tickets", + "TicketPartUpdatedAttributeDataValue": ".tickets", + "TicketPartUpdatedAttributeDataValueId": ".tickets", + "TicketPartUpdatedAttributeDataValueLabel": ".tickets", + "TicketParts": ".types", + "TicketReply": ".types", + "TicketReplyPartType": ".types", + "TicketRequestCustomAttributes": ".types", + "TicketRequestCustomAttributesValue": ".types", + "TicketState": ".tickets", + "TicketStateCategory": ".tickets", + "TicketStateDetailed": ".tickets", + "TicketStateDetailedCategory": ".tickets", + "TicketStateDetailedTicketTypes": ".tickets", + "TicketStateList": ".types", + "TicketType": ".tickets", + "TicketTypeAttribute": ".types", + "TicketTypeAttributeList": ".types", + "TicketTypeCategory": ".tickets", + "TicketTypeList": ".types", + "TicketTypeTicketStates": ".tickets", + "TooManyRequestsError": ".errors", + "Translation": ".types", + "UnauthorizedError": ".errors", + "UnprocessableEntityError": ".errors", + "UntagCompanyRequest": ".types", + "UntagCompanyRequestCompaniesItem": ".types", + "UpdateContactResponse": ".contacts", + "UpdateContentImportSourceRequestStatus": ".ai_content", + "UpdateContentImportSourceRequestSyncBehavior": ".ai_content", + "UpdateDataAttributeRequestBody": ".types", + "UpdateDataAttributeRequestOptions": ".types", + "UpdateDataAttributeRequestOptionsOptionsItem": ".types", + "Visitor": ".types", + "VisitorAvatar": ".types", + "VisitorCompanies": ".types", + "VisitorDeletedObject": ".types", + "VisitorLocationData": ".types", + "VisitorSegments": ".types", + "VisitorSocialProfiles": ".types", + "VisitorTags": ".types", + "VisitorTagsTagsItem": ".types", + "WhatsappMessageStatusList": ".types", + "WhatsappMessageStatusListEventsItem": ".types", + "WhatsappMessageStatusListEventsItemStatus": ".types", + "WhatsappMessageStatusListPages": ".types", + "WhatsappMessageStatusListPagesNext": ".types", + "admins": ".admins", + "ai_agent": ".ai_agent", + "ai_content": ".ai_content", + "ai_content_source": ".ai_content_source", + "articles": ".articles", + "away_status_reasons": ".away_status_reasons", + "brands": ".brands", + "calls": ".calls", + "companies": ".companies", + "contacts": ".contacts", + "conversations": ".conversations", + "custom_channel_events": ".custom_channel_events", + "custom_object_instances": ".custom_object_instances", + "data_attributes": ".data_attributes", + "data_events": ".data_events", + "data_export": ".data_export", + "emails": ".emails", + "export": ".export", + "help_center": ".help_center", + "internal_articles": ".internal_articles", + "jobs": ".jobs", + "macros": ".macros", + "messages": ".messages", + "news": ".news", + "notes": ".notes", + "segments": ".segments", + "subscription_types": ".subscription_types", + "switch": ".switch", + "tags": ".tags", + "teams": ".teams", + "ticket_states": ".ticket_states", + "ticket_type_attributes": ".ticket_type_attributes", + "ticket_types": ".ticket_types", + "tickets": ".tickets", + "visitors": ".visitors", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "ActivityLog", + "ActivityLogActivityType", + "ActivityLogList", + "ActivityLogMetadata", + "ActivityLogPerformedBy", + "AddressableList", + "Admin", + "AdminList", + "AdminPriorityLevel", + "AdminReplyConversationRequest", + "AdminReplyConversationRequestMessageType", + "AdminReplyTicketRequest", + "AdminReplyTicketRequestMessageType", + "AdminReplyTicketRequestReplyOptionsItem", + "AdminWithApp", + "AdminWithAppAvatar", + "AiAgent", + "AiAgentLastAnswerType", + "AiAgentResolutionState", + "AiAgentSourceType", + "AiCallResponse", + "App", + "Article", + "ArticleContent", + "ArticleContentState", + "ArticleList", + "ArticleListItem", + "ArticleListItemState", + "ArticleSearchHighlights", + "ArticleSearchHighlightsHighlightedSummaryItemItem", + "ArticleSearchHighlightsHighlightedSummaryItemItemType", + "ArticleSearchHighlightsHighlightedTitleItem", + "ArticleSearchHighlightsHighlightedTitleItemType", + "ArticleSearchResponse", + "ArticleSearchResponseData", + "ArticleStatistics", + "ArticleTranslatedContent", + "AssignConversationRequest", + "AssignConversationRequestType", + "AttachContactToConversationRequestCustomer", + "AttachContactToConversationRequestCustomerCustomer", + "AttachContactToConversationRequestCustomerIntercomUserId", + "AttachContactToConversationRequestCustomerUserId", + "AwayStatusReason", + "BadRequestError", + "Brand", + "BrandList", + "Call", + "CallList", + "CloseConversationRequest", + "Collection", + "CollectionList", + "Company", + "CompanyAttachedContacts", + "CompanyAttachedSegments", + "CompanyData", + "CompanyList", + "CompanyNote", + "CompanyNoteCompany", + "CompanyNotes", + "CompanyPlan", + "CompanyScroll", + "CompanySegments", + "CompanyTags", + "ConflictError", + "Contact", + "ContactArchived", + "ContactAttachedCompanies", + "ContactAvatar", + "ContactBlocked", + "ContactCompanies", + "ContactDeleted", + "ContactList", + "ContactLocation", + "ContactNotes", + "ContactReference", + "ContactReplyBaseRequest", + "ContactReplyBaseRequestReplyOptionsItem", + "ContactReplyConversationRequest", + "ContactReplyEmailRequest", + "ContactReplyIntercomUserIdRequest", + "ContactReplyTicketEmailRequest", + "ContactReplyTicketIntercomUserIdRequest", + "ContactReplyTicketRequest", + "ContactReplyTicketUserIdRequest", + "ContactReplyUserIdRequest", + "ContactSegments", + "ContactSocialProfiles", + "ContactSubscriptionTypes", + "ContactTags", + "ContactUnarchived", + "ContentImportSource", + "ContentImportSourceStatus", + "ContentImportSourceSyncBehavior", + "ContentImportSourcesList", + "ContentSource", + "ContentSourceContentType", + "ContentSourcesList", + "Conversation", + "ConversationAttachmentFiles", + "ConversationAttributeUpdatedByAdmin", + "ConversationAttributeUpdatedByAdminAttribute", + "ConversationAttributeUpdatedByAdminValue", + "ConversationAttributeUpdatedByWorkflow", + "ConversationAttributeUpdatedByWorkflowAttribute", + "ConversationAttributeUpdatedByWorkflowValue", + "ConversationAttributeUpdatedByWorkflowWorkflow", + "ConversationContacts", + "ConversationDeleted", + "ConversationFirstContactReply", + "ConversationList", + "ConversationPart", + "ConversationPartAuthor", + "ConversationPartMetadata", + "ConversationPartMetadataQuickReplyOptionsItem", + "ConversationPartState", + "ConversationParts", + "ConversationPriority", + "ConversationRating", + "ConversationResponseTime", + "ConversationSource", + "ConversationSourceType", + "ConversationState", + "ConversationStatistics", + "ConversationTeammates", + "CreateArticleRequest", + "CreateArticleRequestState", + "CreateContactResponse", + "CreateContentImportSourceRequestStatus", + "CreateConversationRequestFrom", + "CreateConversationRequestFromType", + "CreateDataAttributeRequest", + "CreateDataAttributeRequestOne", + "CreateDataAttributeRequestOneDataType", + "CreateDataAttributeRequestOptions", + "CreateDataAttributeRequestOptionsOptionsItem", + "CreateDataEventSummariesRequestEventSummaries", + "CreateInternalArticleRequest", + "CreateMessageRequest", + "CreateOrUpdateCompanyRequest", + "CreateOrUpdateTagRequest", + "CreatePhoneSwitchRequest", + "CreateTagRequestBody", + "CreateTicketReplyWithCommentRequest", + "CreateTicketRequestAssignment", + "CreateTicketRequestBody", + "CreateTicketRequestContactsItem", + "CreateTicketRequestContactsItemEmail", + "CreateTicketRequestContactsItemExternalId", + "CreateTicketRequestContactsItemId", + "CreateTicketTypeAttributeRequestDataType", + "CreateTicketTypeRequest", + "CreateTicketTypeRequestCategory", + "CursorPages", + "CustomActionFinished", + "CustomActionFinishedAction", + "CustomActionFinishedActionResult", + "CustomActionStarted", + "CustomActionStartedAction", + "CustomAttributes", + "CustomAttributesValue", + "CustomChannelAttribute", + "CustomChannelBaseEvent", + "CustomChannelContact", + "CustomChannelContactType", + "CustomChannelNotificationResponse", + "CustomObjectInstance", + "CustomObjectInstanceDeleted", + "CustomObjectInstanceList", + "CustomerRequest", + "CustomerRequestEmail", + "CustomerRequestIntercomUserId", + "CustomerRequestUserId", + "DataAttribute", + "DataAttributeDataType", + "DataAttributeList", + "DataAttributeModel", + "DataEvent", + "DataEventList", + "DataEventListPages", + "DataEventSummary", + "DataEventSummaryItem", + "DataExport", + "DataExportCsv", + "DataExportStatus", + "Datetime", + "DeleteTicketResponse", + "DeletedArticleObject", + "DeletedCollectionObject", + "DeletedCompanyObject", + "DeletedInternalArticleObject", + "DeletedObject", + "EmailAddressHeader", + "EmailList", + "EmailMessageMetadata", + "EmailSetting", + "Error", + "ErrorErrorsItem", + "EventDetails", + "ExternalPage", + "ExternalPagesList", + "FileAttribute", + "ForbiddenError", + "GetExportReportingDataGetDatasetsResponse", + "GetExportReportingDataGetDatasetsResponseDataItem", + "GetExportReportingDataGetDatasetsResponseDataItemAttributesItem", + "GroupContent", + "GroupTranslatedContent", + "HelpCenter", + "HelpCenterList", + "InternalArticle", + "InternalArticleList", + "InternalArticleListItem", + "InternalArticleSearchResponse", + "InternalArticleSearchResponseData", + "InternalServerError", + "Jobs", + "JobsStatus", + "LinkedObject", + "LinkedObjectCategory", + "LinkedObjectList", + "LinkedObjectType", + "LisDataAttributesRequestModel", + "LisDataEventsRequestFilter", + "LisDataEventsRequestFilterEmail", + "LisDataEventsRequestFilterIntercomUserId", + "LisDataEventsRequestFilterUserId", + "ListCallsWithTranscriptsResponse", + "ListCallsWithTranscriptsResponseDataItem", + "Macro", + "MacroAvailableOnItem", + "MacroList", + "MacroListPages", + "MacroListPagesNext", + "MacroVisibleTo", + "ManageConversationRequestBody", + "ManageConversationRequestBody_Assignment", + "ManageConversationRequestBody_Close", + "ManageConversationRequestBody_Open", + "ManageConversationRequestBody_Snoozed", + "MergeContactResponse", + "Message", + "MessageMessageType", + "MultipleFilterSearchRequest", + "MultipleFilterSearchRequestOperator", + "MultipleFilterSearchRequestValue", + "NewsItem", + "NewsItemRequest", + "NewsItemRequestState", + "NewsItemState", + "Newsfeed", + "NewsfeedAssignment", + "NotFoundError", + "NotFoundErrorBody", + "NotFoundErrorBodyErrorsItem", + "Note", + "NoteContact", + "NoteList", + "OpenConversationRequest", + "OperatorWorkflowEvent", + "OperatorWorkflowEventEvent", + "OperatorWorkflowEventWorkflow", + "PagesLink", + "PaginatedResponse", + "PaginatedResponseDataItem", + "PaginatedResponseDataItem_NewsItem", + "PaginatedResponseDataItem_Newsfeed", + "PaginatedResponseType", + "PartAttachment", + "PhoneSwitch", + "PostExportReportingDataEnqueueResponse", + "QuickReplyOption", + "Recipient", + "RecipientType", + "RedactConversationRequest", + "RedactConversationRequestConversationPart", + "RedactConversationRequestSource", + "RedactConversationRequest_ConversationPart", + "RedactConversationRequest_Source", + "Reference", + "RegisterFinVoiceCallRequest", + "RegisterFinVoiceCallRequestSource", + "ReplyConversationRequestBody", + "ReplyTicketRequestBody", + "SearchRequest", + "SearchRequestQuery", + "Segment", + "SegmentList", + "SegmentPersonType", + "ShowContactByExternalIdResponse", + "ShowContactResponse", + "SingleFilterSearchRequest", + "SingleFilterSearchRequestOperator", + "SingleFilterSearchRequestValue", + "SingleFilterSearchRequestValueTwoItem", + "SlaApplied", + "SlaAppliedSlaStatus", + "SnoozeConversationRequest", + "SocialProfile", + "StartingAfterPaging", + "SubscriptionType", + "SubscriptionTypeConsentType", + "SubscriptionTypeContentTypesItem", + "SubscriptionTypeList", + "SubscriptionTypeState", + "Tag", + "TagBasic", + "TagCompanyRequest", + "TagCompanyRequestCompaniesItem", + "TagList", + "TagMultipleUsersRequest", + "TagMultipleUsersRequestUsersItem", + "Tags", + "Team", + "TeamList", + "TeamPriorityLevel", + "Ticket", + "TicketCategory", + "TicketContacts", + "TicketCustomAttributes", + "TicketCustomAttributesValue", + "TicketList", + "TicketPart", + "TicketPartAuthor", + "TicketPartAuthorType", + "TicketPartPreviousTicketState", + "TicketPartTicketState", + "TicketPartUpdatedAttributeData", + "TicketPartUpdatedAttributeDataAttribute", + "TicketPartUpdatedAttributeDataValue", + "TicketPartUpdatedAttributeDataValueId", + "TicketPartUpdatedAttributeDataValueLabel", + "TicketParts", + "TicketReply", + "TicketReplyPartType", + "TicketRequestCustomAttributes", + "TicketRequestCustomAttributesValue", + "TicketState", + "TicketStateCategory", + "TicketStateDetailed", + "TicketStateDetailedCategory", + "TicketStateDetailedTicketTypes", + "TicketStateList", + "TicketType", + "TicketTypeAttribute", + "TicketTypeAttributeList", + "TicketTypeCategory", + "TicketTypeList", + "TicketTypeTicketStates", + "TooManyRequestsError", + "Translation", + "UnauthorizedError", + "UnprocessableEntityError", + "UntagCompanyRequest", + "UntagCompanyRequestCompaniesItem", + "UpdateContactResponse", + "UpdateContentImportSourceRequestStatus", + "UpdateContentImportSourceRequestSyncBehavior", + "UpdateDataAttributeRequestBody", + "UpdateDataAttributeRequestOptions", + "UpdateDataAttributeRequestOptionsOptionsItem", + "Visitor", + "VisitorAvatar", + "VisitorCompanies", + "VisitorDeletedObject", + "VisitorLocationData", + "VisitorSegments", + "VisitorSocialProfiles", + "VisitorTags", + "VisitorTagsTagsItem", + "WhatsappMessageStatusList", + "WhatsappMessageStatusListEventsItem", + "WhatsappMessageStatusListEventsItemStatus", + "WhatsappMessageStatusListPages", + "WhatsappMessageStatusListPagesNext", + "admins", + "ai_agent", + "ai_content", + "ai_content_source", + "articles", + "away_status_reasons", + "brands", + "calls", + "companies", + "contacts", + "conversations", + "custom_channel_events", + "custom_object_instances", + "data_attributes", + "data_events", + "data_export", + "emails", + "export", + "help_center", + "internal_articles", + "jobs", + "macros", + "messages", + "news", + "notes", + "segments", + "subscription_types", + "switch", + "tags", + "teams", + "ticket_states", + "ticket_type_attributes", + "ticket_types", + "tickets", + "visitors", +] diff --git a/src/intercom/unstable/admins/__init__.py b/src/intercom/unstable/admins/__init__.py new file mode 100644 index 00000000..bfd65f69 --- /dev/null +++ b/src/intercom/unstable/admins/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import Admin +_dynamic_imports: typing.Dict[str, str] = {"Admin": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Admin"] diff --git a/src/intercom/unstable/admins/client.py b/src/intercom/unstable/admins/client.py new file mode 100644 index 00000000..3759648b --- /dev/null +++ b/src/intercom/unstable/admins/client.py @@ -0,0 +1,470 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..types.activity_log_list import ActivityLogList +from ..types.admin_list import AdminList +from ..types.admin_with_app import AdminWithApp +from .raw_client import AsyncRawAdminsClient, RawAdminsClient +from .types.admin import Admin + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class AdminsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawAdminsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawAdminsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawAdminsClient + """ + return self._raw_client + + def identify_admin( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[AdminWithApp]: + """ + + You can view the currently authorised admin along with the embedded app object (a "workspace" in legacy terminology). + + > 🚧 Single Sign On + > + > If you are building a custom "Log in with Intercom" flow for your site, and you call the `/me` endpoint to identify the logged-in user, you should not accept any sign-ins from users with unverified email addresses as it poses a potential impersonation security risk. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[AdminWithApp] + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.admins.identify_admin() + """ + _response = self._raw_client.identify_admin(request_options=request_options) + return _response.data + + def set_away_admin( + self, + id: int, + *, + away_mode_enabled: bool, + away_mode_reassign: bool, + away_status_reason_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[Admin]: + """ + You can set an Admin as away for the Inbox. + + Parameters + ---------- + id : int + The unique identifier of a given admin + + away_mode_enabled : bool + Set to "true" to change the status of the admin to away. + + away_mode_reassign : bool + Set to "true" to assign any new conversation replies to your default inbox. + + away_status_reason_id : typing.Optional[int] + The unique identifier of the away status reason + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Admin] + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.admins.set_away_admin( + id=1, + away_mode_enabled=True, + away_mode_reassign=True, + away_status_reason_id=12345, + ) + """ + _response = self._raw_client.set_away_admin( + id, + away_mode_enabled=away_mode_enabled, + away_mode_reassign=away_mode_reassign, + away_status_reason_id=away_status_reason_id, + request_options=request_options, + ) + return _response.data + + def list_activity_logs( + self, + *, + created_at_after: str, + created_at_before: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ActivityLogList: + """ + You can get a log of activities by all admins in an app. + + Parameters + ---------- + created_at_after : str + The start date that you request data for. It must be formatted as a UNIX timestamp. + + created_at_before : typing.Optional[str] + The end date that you request data for. It must be formatted as a UNIX timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ActivityLogList + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.admins.list_activity_logs( + created_at_after="1677253093", + created_at_before="1677861493", + ) + """ + _response = self._raw_client.list_activity_logs( + created_at_after=created_at_after, created_at_before=created_at_before, request_options=request_options + ) + return _response.data + + def list_admins(self, *, request_options: typing.Optional[RequestOptions] = None) -> AdminList: + """ + You can fetch a list of admins for a given workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AdminList + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.admins.list_admins() + """ + _response = self._raw_client.list_admins(request_options=request_options) + return _response.data + + def retrieve_admin( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[Admin]: + """ + You can retrieve the details of a single admin. + + Parameters + ---------- + id : int + The unique identifier of a given admin + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Admin] + Admin found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.admins.retrieve_admin( + id=1, + ) + """ + _response = self._raw_client.retrieve_admin(id, request_options=request_options) + return _response.data + + +class AsyncAdminsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawAdminsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawAdminsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawAdminsClient + """ + return self._raw_client + + async def identify_admin( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[AdminWithApp]: + """ + + You can view the currently authorised admin along with the embedded app object (a "workspace" in legacy terminology). + + > 🚧 Single Sign On + > + > If you are building a custom "Log in with Intercom" flow for your site, and you call the `/me` endpoint to identify the logged-in user, you should not accept any sign-ins from users with unverified email addresses as it poses a potential impersonation security risk. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[AdminWithApp] + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.admins.identify_admin() + + + asyncio.run(main()) + """ + _response = await self._raw_client.identify_admin(request_options=request_options) + return _response.data + + async def set_away_admin( + self, + id: int, + *, + away_mode_enabled: bool, + away_mode_reassign: bool, + away_status_reason_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[Admin]: + """ + You can set an Admin as away for the Inbox. + + Parameters + ---------- + id : int + The unique identifier of a given admin + + away_mode_enabled : bool + Set to "true" to change the status of the admin to away. + + away_mode_reassign : bool + Set to "true" to assign any new conversation replies to your default inbox. + + away_status_reason_id : typing.Optional[int] + The unique identifier of the away status reason + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Admin] + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.admins.set_away_admin( + id=1, + away_mode_enabled=True, + away_mode_reassign=True, + away_status_reason_id=12345, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.set_away_admin( + id, + away_mode_enabled=away_mode_enabled, + away_mode_reassign=away_mode_reassign, + away_status_reason_id=away_status_reason_id, + request_options=request_options, + ) + return _response.data + + async def list_activity_logs( + self, + *, + created_at_after: str, + created_at_before: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ActivityLogList: + """ + You can get a log of activities by all admins in an app. + + Parameters + ---------- + created_at_after : str + The start date that you request data for. It must be formatted as a UNIX timestamp. + + created_at_before : typing.Optional[str] + The end date that you request data for. It must be formatted as a UNIX timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ActivityLogList + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.admins.list_activity_logs( + created_at_after="1677253093", + created_at_before="1677861493", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_activity_logs( + created_at_after=created_at_after, created_at_before=created_at_before, request_options=request_options + ) + return _response.data + + async def list_admins(self, *, request_options: typing.Optional[RequestOptions] = None) -> AdminList: + """ + You can fetch a list of admins for a given workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AdminList + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.admins.list_admins() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_admins(request_options=request_options) + return _response.data + + async def retrieve_admin( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[Admin]: + """ + You can retrieve the details of a single admin. + + Parameters + ---------- + id : int + The unique identifier of a given admin + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Admin] + Admin found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.admins.retrieve_admin( + id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.retrieve_admin(id, request_options=request_options) + return _response.data diff --git a/src/intercom/unstable/admins/raw_client.py b/src/intercom/unstable/admins/raw_client.py new file mode 100644 index 00000000..97865e38 --- /dev/null +++ b/src/intercom/unstable/admins/raw_client.py @@ -0,0 +1,654 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.bad_request_error import BadRequestError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.activity_log_list import ActivityLogList +from ..types.admin_list import AdminList +from ..types.admin_with_app import AdminWithApp +from ..types.error import Error +from .types.admin import Admin + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawAdminsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def identify_admin( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[typing.Optional[AdminWithApp]]: + """ + + You can view the currently authorised admin along with the embedded app object (a "workspace" in legacy terminology). + + > 🚧 Single Sign On + > + > If you are building a custom "Log in with Intercom" flow for your site, and you call the `/me` endpoint to identify the logged-in user, you should not accept any sign-ins from users with unverified email addresses as it poses a potential impersonation security risk. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[AdminWithApp]] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "me", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[AdminWithApp], + construct_type( + type_=typing.Optional[AdminWithApp], # 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 set_away_admin( + self, + id: int, + *, + away_mode_enabled: bool, + away_mode_reassign: bool, + away_status_reason_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[typing.Optional[Admin]]: + """ + You can set an Admin as away for the Inbox. + + Parameters + ---------- + id : int + The unique identifier of a given admin + + away_mode_enabled : bool + Set to "true" to change the status of the admin to away. + + away_mode_reassign : bool + Set to "true" to assign any new conversation replies to your default inbox. + + away_status_reason_id : typing.Optional[int] + The unique identifier of the away status reason + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[Admin]] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + f"admins/{jsonable_encoder(id)}/away", + method="PUT", + json={ + "away_mode_enabled": away_mode_enabled, + "away_mode_reassign": away_mode_reassign, + "away_status_reason_id": away_status_reason_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Admin], + construct_type( + type_=typing.Optional[Admin], # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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_activity_logs( + self, + *, + created_at_after: str, + created_at_before: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ActivityLogList]: + """ + You can get a log of activities by all admins in an app. + + Parameters + ---------- + created_at_after : str + The start date that you request data for. It must be formatted as a UNIX timestamp. + + created_at_before : typing.Optional[str] + The end date that you request data for. It must be formatted as a UNIX timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ActivityLogList] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "admins/activity_logs", + method="GET", + params={ + "created_at_after": created_at_after, + "created_at_before": created_at_before, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ActivityLogList, + construct_type( + type_=ActivityLogList, # 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 list_admins(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[AdminList]: + """ + You can fetch a list of admins for a given workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[AdminList] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "admins", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AdminList, + construct_type( + type_=AdminList, # 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 retrieve_admin( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[typing.Optional[Admin]]: + """ + You can retrieve the details of a single admin. + + Parameters + ---------- + id : int + The unique identifier of a given admin + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[Admin]] + Admin found + """ + _response = self._client_wrapper.httpx_client.request( + f"admins/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Admin], + construct_type( + type_=typing.Optional[Admin], # 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) + + +class AsyncRawAdminsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def identify_admin( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[typing.Optional[AdminWithApp]]: + """ + + You can view the currently authorised admin along with the embedded app object (a "workspace" in legacy terminology). + + > 🚧 Single Sign On + > + > If you are building a custom "Log in with Intercom" flow for your site, and you call the `/me` endpoint to identify the logged-in user, you should not accept any sign-ins from users with unverified email addresses as it poses a potential impersonation security risk. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[AdminWithApp]] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "me", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[AdminWithApp], + construct_type( + type_=typing.Optional[AdminWithApp], # 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 set_away_admin( + self, + id: int, + *, + away_mode_enabled: bool, + away_mode_reassign: bool, + away_status_reason_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[typing.Optional[Admin]]: + """ + You can set an Admin as away for the Inbox. + + Parameters + ---------- + id : int + The unique identifier of a given admin + + away_mode_enabled : bool + Set to "true" to change the status of the admin to away. + + away_mode_reassign : bool + Set to "true" to assign any new conversation replies to your default inbox. + + away_status_reason_id : typing.Optional[int] + The unique identifier of the away status reason + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[Admin]] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + f"admins/{jsonable_encoder(id)}/away", + method="PUT", + json={ + "away_mode_enabled": away_mode_enabled, + "away_mode_reassign": away_mode_reassign, + "away_status_reason_id": away_status_reason_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Admin], + construct_type( + type_=typing.Optional[Admin], # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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_activity_logs( + self, + *, + created_at_after: str, + created_at_before: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ActivityLogList]: + """ + You can get a log of activities by all admins in an app. + + Parameters + ---------- + created_at_after : str + The start date that you request data for. It must be formatted as a UNIX timestamp. + + created_at_before : typing.Optional[str] + The end date that you request data for. It must be formatted as a UNIX timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ActivityLogList] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "admins/activity_logs", + method="GET", + params={ + "created_at_after": created_at_after, + "created_at_before": created_at_before, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ActivityLogList, + construct_type( + type_=ActivityLogList, # 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 list_admins( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[AdminList]: + """ + You can fetch a list of admins for a given workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[AdminList] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "admins", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AdminList, + construct_type( + type_=AdminList, # 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 retrieve_admin( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[typing.Optional[Admin]]: + """ + You can retrieve the details of a single admin. + + Parameters + ---------- + id : int + The unique identifier of a given admin + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[Admin]] + Admin found + """ + _response = await self._client_wrapper.httpx_client.request( + f"admins/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Admin], + construct_type( + type_=typing.Optional[Admin], # 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) diff --git a/src/intercom/unstable/admins/types/__init__.py b/src/intercom/unstable/admins/types/__init__.py new file mode 100644 index 00000000..da3bf4a6 --- /dev/null +++ b/src/intercom/unstable/admins/types/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .admin import Admin +_dynamic_imports: typing.Dict[str, str] = {"Admin": ".admin"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Admin"] diff --git a/src/intercom/unstable/admins/types/admin.py b/src/intercom/unstable/admins/types/admin.py new file mode 100644 index 00000000..8972bf24 --- /dev/null +++ b/src/intercom/unstable/admins/types/admin.py @@ -0,0 +1,80 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...types.team_priority_level import TeamPriorityLevel + + +class Admin(UncheckedBaseModel): + """ + Admins are teammate accounts that have access to a workspace. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `admin`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the admin. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the admin. + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + The email of the admin. + """ + + job_title: typing.Optional[str] = pydantic.Field(default=None) + """ + The job title of the admin. + """ + + away_mode_enabled: typing.Optional[bool] = pydantic.Field(default=None) + """ + Identifies if this admin is currently set in away mode. + """ + + away_mode_reassign: typing.Optional[bool] = pydantic.Field(default=None) + """ + Identifies if this admin is set to automatically reassign new conversations to the apps default inbox. + """ + + away_status_reason_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The unique identifier of the away status reason + """ + + has_inbox_seat: typing.Optional[bool] = pydantic.Field(default=None) + """ + Identifies if this admin has a paid inbox seat to restrict/allow features that require them. + """ + + team_ids: typing.Optional[typing.List[int]] = pydantic.Field(default=None) + """ + This object represents the avatar associated with the admin. + """ + + avatar: typing.Optional[str] = pydantic.Field(default=None) + """ + Image for the associated team or teammate + """ + + team_priority_level: typing.Optional[TeamPriorityLevel] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/ai_agent/__init__.py b/src/intercom/unstable/ai_agent/__init__.py new file mode 100644 index 00000000..f338d84d --- /dev/null +++ b/src/intercom/unstable/ai_agent/__init__.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import AiAgent, AiAgentLastAnswerType, AiAgentResolutionState, AiAgentSourceType +_dynamic_imports: typing.Dict[str, str] = { + "AiAgent": ".types", + "AiAgentLastAnswerType": ".types", + "AiAgentResolutionState": ".types", + "AiAgentSourceType": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["AiAgent", "AiAgentLastAnswerType", "AiAgentResolutionState", "AiAgentSourceType"] diff --git a/src/intercom/unstable/ai_agent/types/__init__.py b/src/intercom/unstable/ai_agent/types/__init__.py new file mode 100644 index 00000000..e869e949 --- /dev/null +++ b/src/intercom/unstable/ai_agent/types/__init__.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .ai_agent import AiAgent + from .ai_agent_last_answer_type import AiAgentLastAnswerType + from .ai_agent_resolution_state import AiAgentResolutionState + from .ai_agent_source_type import AiAgentSourceType +_dynamic_imports: typing.Dict[str, str] = { + "AiAgent": ".ai_agent", + "AiAgentLastAnswerType": ".ai_agent_last_answer_type", + "AiAgentResolutionState": ".ai_agent_resolution_state", + "AiAgentSourceType": ".ai_agent_source_type", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["AiAgent", "AiAgentLastAnswerType", "AiAgentResolutionState", "AiAgentSourceType"] diff --git a/src/intercom/unstable/ai_agent/types/ai_agent.py b/src/intercom/unstable/ai_agent/types/ai_agent.py new file mode 100644 index 00000000..afe04c9c --- /dev/null +++ b/src/intercom/unstable/ai_agent/types/ai_agent.py @@ -0,0 +1,68 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...types.content_sources_list import ContentSourcesList +from .ai_agent_last_answer_type import AiAgentLastAnswerType +from .ai_agent_resolution_state import AiAgentResolutionState +from .ai_agent_source_type import AiAgentSourceType + + +class AiAgent(UncheckedBaseModel): + """ + Data related to AI Agent involvement in the conversation. + """ + + source_type: typing.Optional[AiAgentSourceType] = pydantic.Field(default=None) + """ + The type of the source that triggered AI Agent involvement in the conversation. + """ + + source_title: typing.Optional[str] = pydantic.Field(default=None) + """ + The title of the source that triggered AI Agent involvement in the conversation. If this is `essentials_plan_setup` then it will return `null`. + """ + + last_answer_type: typing.Optional[AiAgentLastAnswerType] = pydantic.Field(default=None) + """ + The type of the last answer delivered by AI Agent. If no answer was delivered then this will return `null` + """ + + resolution_state: typing.Optional[AiAgentResolutionState] = pydantic.Field(default=None) + """ + The resolution state of AI Agent. If no AI or custom answer has been delivered then this will return `null`. + """ + + rating: typing.Optional[int] = pydantic.Field(default=None) + """ + The customer satisfaction rating given to AI Agent, from 1-5. + """ + + rating_remark: typing.Optional[str] = pydantic.Field(default=None) + """ + The customer satisfaction rating remark given to AI Agent. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the AI agent rating was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the AI agent rating was last updated. + """ + + content_sources: typing.Optional[ContentSourcesList] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/ai_agent/types/ai_agent_last_answer_type.py b/src/intercom/unstable/ai_agent/types/ai_agent_last_answer_type.py new file mode 100644 index 00000000..ab8e9a15 --- /dev/null +++ b/src/intercom/unstable/ai_agent/types/ai_agent_last_answer_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AiAgentLastAnswerType = typing.Union[typing.Literal["ai_answer", "custom_answer"], typing.Any] diff --git a/src/intercom/unstable/ai_agent/types/ai_agent_resolution_state.py b/src/intercom/unstable/ai_agent/types/ai_agent_resolution_state.py new file mode 100644 index 00000000..33f3d410 --- /dev/null +++ b/src/intercom/unstable/ai_agent/types/ai_agent_resolution_state.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AiAgentResolutionState = typing.Union[ + typing.Literal["assumed_resolution", "confirmed_resolution", "escalated", "negative_feedback"], typing.Any +] diff --git a/src/intercom/unstable/ai_agent/types/ai_agent_source_type.py b/src/intercom/unstable/ai_agent/types/ai_agent_source_type.py new file mode 100644 index 00000000..f108bc8c --- /dev/null +++ b/src/intercom/unstable/ai_agent/types/ai_agent_source_type.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AiAgentSourceType = typing.Union[ + typing.Literal["essentials_plan_setup", "profile", "workflow", "workflow_preview", "fin_preview"], typing.Any +] diff --git a/src/intercom/unstable/ai_content/__init__.py b/src/intercom/unstable/ai_content/__init__.py new file mode 100644 index 00000000..c16e040f --- /dev/null +++ b/src/intercom/unstable/ai_content/__init__.py @@ -0,0 +1,64 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + ContentImportSource, + ContentImportSourceStatus, + ContentImportSourceSyncBehavior, + ContentImportSourcesList, + CreateContentImportSourceRequestStatus, + ExternalPage, + ExternalPagesList, + UpdateContentImportSourceRequestStatus, + UpdateContentImportSourceRequestSyncBehavior, + ) +_dynamic_imports: typing.Dict[str, str] = { + "ContentImportSource": ".types", + "ContentImportSourceStatus": ".types", + "ContentImportSourceSyncBehavior": ".types", + "ContentImportSourcesList": ".types", + "CreateContentImportSourceRequestStatus": ".types", + "ExternalPage": ".types", + "ExternalPagesList": ".types", + "UpdateContentImportSourceRequestStatus": ".types", + "UpdateContentImportSourceRequestSyncBehavior": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "ContentImportSource", + "ContentImportSourceStatus", + "ContentImportSourceSyncBehavior", + "ContentImportSourcesList", + "CreateContentImportSourceRequestStatus", + "ExternalPage", + "ExternalPagesList", + "UpdateContentImportSourceRequestStatus", + "UpdateContentImportSourceRequestSyncBehavior", +] diff --git a/src/intercom/unstable/ai_content/client.py b/src/intercom/unstable/ai_content/client.py new file mode 100644 index 00000000..7d2ed8ad --- /dev/null +++ b/src/intercom/unstable/ai_content/client.py @@ -0,0 +1,973 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from .raw_client import AsyncRawAiContentClient, RawAiContentClient +from .types.content_import_source import ContentImportSource +from .types.content_import_sources_list import ContentImportSourcesList +from .types.create_content_import_source_request_status import CreateContentImportSourceRequestStatus +from .types.external_page import ExternalPage +from .types.external_pages_list import ExternalPagesList +from .types.update_content_import_source_request_status import UpdateContentImportSourceRequestStatus +from .types.update_content_import_source_request_sync_behavior import UpdateContentImportSourceRequestSyncBehavior + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class AiContentClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawAiContentClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawAiContentClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawAiContentClient + """ + return self._raw_client + + def list_content_import_sources( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContentImportSourcesList: + """ + You can retrieve a list of all content import sources for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContentImportSourcesList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.ai_content.list_content_import_sources() + """ + _response = self._raw_client.list_content_import_sources(request_options=request_options) + return _response.data + + def create_content_import_source( + self, + *, + url: str, + status: typing.Optional[CreateContentImportSourceRequestStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ContentImportSource: + """ + You can create a new content import source by sending a POST request to this endpoint. + + Parameters + ---------- + url : str + The URL of the content import source. + + status : typing.Optional[CreateContentImportSourceRequestStatus] + The status of the content import source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContentImportSource + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.ai_content.create_content_import_source( + url="https://www.example.com", + ) + """ + _response = self._raw_client.create_content_import_source( + url=url, status=status, request_options=request_options + ) + return _response.data + + def get_content_import_source( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContentImportSource: + """ + Parameters + ---------- + id : str + The unique identifier for the content import source which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContentImportSource + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.ai_content.get_content_import_source( + id="id", + ) + """ + _response = self._raw_client.get_content_import_source(id, request_options=request_options) + return _response.data + + def update_content_import_source( + self, + id: str, + *, + sync_behavior: UpdateContentImportSourceRequestSyncBehavior, + url: str, + status: typing.Optional[UpdateContentImportSourceRequestStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ContentImportSource: + """ + You can update an existing content import source. + + Parameters + ---------- + id : str + The unique identifier for the content import source which is given by Intercom. + + sync_behavior : UpdateContentImportSourceRequestSyncBehavior + If you intend to create or update External Pages via the API, this should be set to `api`. You can not change the value to or from api. + + url : str + The URL of the content import source. This may only be different from the existing value if the sync behavior is API. + + status : typing.Optional[UpdateContentImportSourceRequestStatus] + The status of the content import source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContentImportSource + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.ai_content.update_content_import_source( + id="id", + sync_behavior="api", + url="https://www.example.com", + ) + """ + _response = self._raw_client.update_content_import_source( + id, sync_behavior=sync_behavior, url=url, status=status, request_options=request_options + ) + return _response.data + + def delete_content_import_source(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + You can delete a content import source by making a DELETE request this endpoint. This will also delete all external pages that were imported from this source. + + Parameters + ---------- + id : str + The unique identifier for the content import source which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.ai_content.delete_content_import_source( + id="id", + ) + """ + _response = self._raw_client.delete_content_import_source(id, request_options=request_options) + return _response.data + + def list_external_pages(self, *, request_options: typing.Optional[RequestOptions] = None) -> ExternalPagesList: + """ + You can retrieve a list of all external pages for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ExternalPagesList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.ai_content.list_external_pages() + """ + _response = self._raw_client.list_external_pages(request_options=request_options) + return _response.data + + def create_external_page( + self, + *, + title: str, + html: str, + source_id: int, + external_id: str, + url: typing.Optional[str] = OMIT, + ai_agent_availability: typing.Optional[bool] = OMIT, + ai_copilot_availability: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ExternalPage: + """ + You can create a new external page by sending a POST request to this endpoint. If an external page already exists with the specified source_id and external_id, it will be updated instead. + + Parameters + ---------- + title : str + The title of the external page. + + html : str + The body of the external page in HTML. + + source_id : int + The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + + external_id : str + The identifier for the external page which was given by the source. Must be unique for the source. + + url : typing.Optional[str] + The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. When a URL is not present, Fin will not reference the source. + + ai_agent_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by AI Agent. Will not default when updating an existing external page. + + ai_copilot_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by AI Copilot. Will not default when updating an existing external page. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ExternalPage + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.ai_content.create_external_page( + title="Test", + html="

Test

", + url="https://www.example.com", + source_id=44, + external_id="abc1234", + ) + """ + _response = self._raw_client.create_external_page( + title=title, + html=html, + source_id=source_id, + external_id=external_id, + url=url, + ai_agent_availability=ai_agent_availability, + ai_copilot_availability=ai_copilot_availability, + request_options=request_options, + ) + return _response.data + + def get_external_page(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> ExternalPage: + """ + You can retrieve an external page. + + Parameters + ---------- + id : str + The unique identifier for the external page which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ExternalPage + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.ai_content.get_external_page( + id="id", + ) + """ + _response = self._raw_client.get_external_page(id, request_options=request_options) + return _response.data + + def update_external_page( + self, + id: str, + *, + title: str, + html: str, + url: str, + source_id: int, + fin_availability: typing.Optional[bool] = OMIT, + external_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ExternalPage: + """ + You can update an existing external page (if it was created via the API). + + Parameters + ---------- + id : str + The unique identifier for the external page which is given by Intercom. + + title : str + The title of the external page. + + html : str + The body of the external page in HTML. + + url : str + The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. + + source_id : int + The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + + fin_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by Fin. + + external_id : typing.Optional[str] + The identifier for the external page which was given by the source. Must be unique for the source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ExternalPage + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.ai_content.update_external_page( + id="id", + title="Test", + html="

Test

", + url="https://www.example.com", + source_id=47, + external_id="5678", + ) + """ + _response = self._raw_client.update_external_page( + id, + title=title, + html=html, + url=url, + source_id=source_id, + fin_availability=fin_availability, + external_id=external_id, + request_options=request_options, + ) + return _response.data + + def delete_external_page(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> ExternalPage: + """ + Sending a DELETE request for an external page will remove it from the content library UI and from being used for AI answers. + + Parameters + ---------- + id : str + The unique identifier for the external page which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ExternalPage + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.ai_content.delete_external_page( + id="id", + ) + """ + _response = self._raw_client.delete_external_page(id, request_options=request_options) + return _response.data + + +class AsyncAiContentClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawAiContentClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawAiContentClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawAiContentClient + """ + return self._raw_client + + async def list_content_import_sources( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContentImportSourcesList: + """ + You can retrieve a list of all content import sources for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContentImportSourcesList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.ai_content.list_content_import_sources() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_content_import_sources(request_options=request_options) + return _response.data + + async def create_content_import_source( + self, + *, + url: str, + status: typing.Optional[CreateContentImportSourceRequestStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ContentImportSource: + """ + You can create a new content import source by sending a POST request to this endpoint. + + Parameters + ---------- + url : str + The URL of the content import source. + + status : typing.Optional[CreateContentImportSourceRequestStatus] + The status of the content import source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContentImportSource + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.ai_content.create_content_import_source( + url="https://www.example.com", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_content_import_source( + url=url, status=status, request_options=request_options + ) + return _response.data + + async def get_content_import_source( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContentImportSource: + """ + Parameters + ---------- + id : str + The unique identifier for the content import source which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContentImportSource + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.ai_content.get_content_import_source( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_content_import_source(id, request_options=request_options) + return _response.data + + async def update_content_import_source( + self, + id: str, + *, + sync_behavior: UpdateContentImportSourceRequestSyncBehavior, + url: str, + status: typing.Optional[UpdateContentImportSourceRequestStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ContentImportSource: + """ + You can update an existing content import source. + + Parameters + ---------- + id : str + The unique identifier for the content import source which is given by Intercom. + + sync_behavior : UpdateContentImportSourceRequestSyncBehavior + If you intend to create or update External Pages via the API, this should be set to `api`. You can not change the value to or from api. + + url : str + The URL of the content import source. This may only be different from the existing value if the sync behavior is API. + + status : typing.Optional[UpdateContentImportSourceRequestStatus] + The status of the content import source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContentImportSource + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.ai_content.update_content_import_source( + id="id", + sync_behavior="api", + url="https://www.example.com", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update_content_import_source( + id, sync_behavior=sync_behavior, url=url, status=status, request_options=request_options + ) + return _response.data + + async def delete_content_import_source( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + You can delete a content import source by making a DELETE request this endpoint. This will also delete all external pages that were imported from this source. + + Parameters + ---------- + id : str + The unique identifier for the content import source which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.ai_content.delete_content_import_source( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_content_import_source(id, request_options=request_options) + return _response.data + + async def list_external_pages( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> ExternalPagesList: + """ + You can retrieve a list of all external pages for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ExternalPagesList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.ai_content.list_external_pages() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_external_pages(request_options=request_options) + return _response.data + + async def create_external_page( + self, + *, + title: str, + html: str, + source_id: int, + external_id: str, + url: typing.Optional[str] = OMIT, + ai_agent_availability: typing.Optional[bool] = OMIT, + ai_copilot_availability: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ExternalPage: + """ + You can create a new external page by sending a POST request to this endpoint. If an external page already exists with the specified source_id and external_id, it will be updated instead. + + Parameters + ---------- + title : str + The title of the external page. + + html : str + The body of the external page in HTML. + + source_id : int + The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + + external_id : str + The identifier for the external page which was given by the source. Must be unique for the source. + + url : typing.Optional[str] + The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. When a URL is not present, Fin will not reference the source. + + ai_agent_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by AI Agent. Will not default when updating an existing external page. + + ai_copilot_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by AI Copilot. Will not default when updating an existing external page. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ExternalPage + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.ai_content.create_external_page( + title="Test", + html="

Test

", + url="https://www.example.com", + source_id=44, + external_id="abc1234", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_external_page( + title=title, + html=html, + source_id=source_id, + external_id=external_id, + url=url, + ai_agent_availability=ai_agent_availability, + ai_copilot_availability=ai_copilot_availability, + request_options=request_options, + ) + return _response.data + + async def get_external_page( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ExternalPage: + """ + You can retrieve an external page. + + Parameters + ---------- + id : str + The unique identifier for the external page which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ExternalPage + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.ai_content.get_external_page( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_external_page(id, request_options=request_options) + return _response.data + + async def update_external_page( + self, + id: str, + *, + title: str, + html: str, + url: str, + source_id: int, + fin_availability: typing.Optional[bool] = OMIT, + external_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ExternalPage: + """ + You can update an existing external page (if it was created via the API). + + Parameters + ---------- + id : str + The unique identifier for the external page which is given by Intercom. + + title : str + The title of the external page. + + html : str + The body of the external page in HTML. + + url : str + The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. + + source_id : int + The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + + fin_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by Fin. + + external_id : typing.Optional[str] + The identifier for the external page which was given by the source. Must be unique for the source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ExternalPage + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.ai_content.update_external_page( + id="id", + title="Test", + html="

Test

", + url="https://www.example.com", + source_id=47, + external_id="5678", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update_external_page( + id, + title=title, + html=html, + url=url, + source_id=source_id, + fin_availability=fin_availability, + external_id=external_id, + request_options=request_options, + ) + return _response.data + + async def delete_external_page( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ExternalPage: + """ + Sending a DELETE request for an external page will remove it from the content library UI and from being used for AI answers. + + Parameters + ---------- + id : str + The unique identifier for the external page which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ExternalPage + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.ai_content.delete_external_page( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_external_page(id, request_options=request_options) + return _response.data diff --git a/src/intercom/unstable/ai_content/raw_client.py b/src/intercom/unstable/ai_content/raw_client.py new file mode 100644 index 00000000..6880e7c3 --- /dev/null +++ b/src/intercom/unstable/ai_content/raw_client.py @@ -0,0 +1,1243 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from .types.content_import_source import ContentImportSource +from .types.content_import_sources_list import ContentImportSourcesList +from .types.create_content_import_source_request_status import CreateContentImportSourceRequestStatus +from .types.external_page import ExternalPage +from .types.external_pages_list import ExternalPagesList +from .types.update_content_import_source_request_status import UpdateContentImportSourceRequestStatus +from .types.update_content_import_source_request_sync_behavior import UpdateContentImportSourceRequestSyncBehavior + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawAiContentClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_content_import_sources( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ContentImportSourcesList]: + """ + You can retrieve a list of all content import sources for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ContentImportSourcesList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "ai/content_import_sources", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ContentImportSourcesList, + construct_type( + type_=ContentImportSourcesList, # 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 create_content_import_source( + self, + *, + url: str, + status: typing.Optional[CreateContentImportSourceRequestStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ContentImportSource]: + """ + You can create a new content import source by sending a POST request to this endpoint. + + Parameters + ---------- + url : str + The URL of the content import source. + + status : typing.Optional[CreateContentImportSourceRequestStatus] + The status of the content import source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ContentImportSource] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "ai/content_import_sources", + method="POST", + json={ + "status": status, + "url": url, + "sync_behavior": "api", + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ContentImportSource, + construct_type( + type_=ContentImportSource, # 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 get_content_import_source( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ContentImportSource]: + """ + Parameters + ---------- + id : str + The unique identifier for the content import source which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ContentImportSource] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"ai/content_import_sources/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ContentImportSource, + construct_type( + type_=ContentImportSource, # 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_content_import_source( + self, + id: str, + *, + sync_behavior: UpdateContentImportSourceRequestSyncBehavior, + url: str, + status: typing.Optional[UpdateContentImportSourceRequestStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ContentImportSource]: + """ + You can update an existing content import source. + + Parameters + ---------- + id : str + The unique identifier for the content import source which is given by Intercom. + + sync_behavior : UpdateContentImportSourceRequestSyncBehavior + If you intend to create or update External Pages via the API, this should be set to `api`. You can not change the value to or from api. + + url : str + The URL of the content import source. This may only be different from the existing value if the sync behavior is API. + + status : typing.Optional[UpdateContentImportSourceRequestStatus] + The status of the content import source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ContentImportSource] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"ai/content_import_sources/{jsonable_encoder(id)}", + method="PUT", + json={ + "sync_behavior": sync_behavior, + "status": status, + "url": url, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ContentImportSource, + construct_type( + type_=ContentImportSource, # 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_content_import_source( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[None]: + """ + You can delete a content import source by making a DELETE request this endpoint. This will also delete all external pages that were imported from this source. + + Parameters + ---------- + id : str + The unique identifier for the content import source which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + f"ai/content_import_sources/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + 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_external_pages( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ExternalPagesList]: + """ + You can retrieve a list of all external pages for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ExternalPagesList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "ai/external_pages", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ExternalPagesList, + construct_type( + type_=ExternalPagesList, # 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 create_external_page( + self, + *, + title: str, + html: str, + source_id: int, + external_id: str, + url: typing.Optional[str] = OMIT, + ai_agent_availability: typing.Optional[bool] = OMIT, + ai_copilot_availability: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ExternalPage]: + """ + You can create a new external page by sending a POST request to this endpoint. If an external page already exists with the specified source_id and external_id, it will be updated instead. + + Parameters + ---------- + title : str + The title of the external page. + + html : str + The body of the external page in HTML. + + source_id : int + The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + + external_id : str + The identifier for the external page which was given by the source. Must be unique for the source. + + url : typing.Optional[str] + The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. When a URL is not present, Fin will not reference the source. + + ai_agent_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by AI Agent. Will not default when updating an existing external page. + + ai_copilot_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by AI Copilot. Will not default when updating an existing external page. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ExternalPage] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "ai/external_pages", + method="POST", + json={ + "title": title, + "html": html, + "url": url, + "ai_agent_availability": ai_agent_availability, + "ai_copilot_availability": ai_copilot_availability, + "source_id": source_id, + "external_id": external_id, + "locale": "en", + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ExternalPage, + construct_type( + type_=ExternalPage, # 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 get_external_page( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ExternalPage]: + """ + You can retrieve an external page. + + Parameters + ---------- + id : str + The unique identifier for the external page which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ExternalPage] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"ai/external_pages/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ExternalPage, + construct_type( + type_=ExternalPage, # 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_external_page( + self, + id: str, + *, + title: str, + html: str, + url: str, + source_id: int, + fin_availability: typing.Optional[bool] = OMIT, + external_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ExternalPage]: + """ + You can update an existing external page (if it was created via the API). + + Parameters + ---------- + id : str + The unique identifier for the external page which is given by Intercom. + + title : str + The title of the external page. + + html : str + The body of the external page in HTML. + + url : str + The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. + + source_id : int + The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + + fin_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by Fin. + + external_id : typing.Optional[str] + The identifier for the external page which was given by the source. Must be unique for the source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ExternalPage] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"ai/external_pages/{jsonable_encoder(id)}", + method="PUT", + json={ + "title": title, + "html": html, + "url": url, + "fin_availability": fin_availability, + "source_id": source_id, + "external_id": external_id, + "locale": "en", + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ExternalPage, + construct_type( + type_=ExternalPage, # 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_external_page( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ExternalPage]: + """ + Sending a DELETE request for an external page will remove it from the content library UI and from being used for AI answers. + + Parameters + ---------- + id : str + The unique identifier for the external page which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ExternalPage] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"ai/external_pages/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ExternalPage, + construct_type( + type_=ExternalPage, # 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) + + +class AsyncRawAiContentClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_content_import_sources( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ContentImportSourcesList]: + """ + You can retrieve a list of all content import sources for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ContentImportSourcesList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "ai/content_import_sources", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ContentImportSourcesList, + construct_type( + type_=ContentImportSourcesList, # 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 create_content_import_source( + self, + *, + url: str, + status: typing.Optional[CreateContentImportSourceRequestStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ContentImportSource]: + """ + You can create a new content import source by sending a POST request to this endpoint. + + Parameters + ---------- + url : str + The URL of the content import source. + + status : typing.Optional[CreateContentImportSourceRequestStatus] + The status of the content import source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ContentImportSource] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "ai/content_import_sources", + method="POST", + json={ + "status": status, + "url": url, + "sync_behavior": "api", + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ContentImportSource, + construct_type( + type_=ContentImportSource, # 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 get_content_import_source( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ContentImportSource]: + """ + Parameters + ---------- + id : str + The unique identifier for the content import source which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ContentImportSource] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"ai/content_import_sources/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ContentImportSource, + construct_type( + type_=ContentImportSource, # 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_content_import_source( + self, + id: str, + *, + sync_behavior: UpdateContentImportSourceRequestSyncBehavior, + url: str, + status: typing.Optional[UpdateContentImportSourceRequestStatus] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ContentImportSource]: + """ + You can update an existing content import source. + + Parameters + ---------- + id : str + The unique identifier for the content import source which is given by Intercom. + + sync_behavior : UpdateContentImportSourceRequestSyncBehavior + If you intend to create or update External Pages via the API, this should be set to `api`. You can not change the value to or from api. + + url : str + The URL of the content import source. This may only be different from the existing value if the sync behavior is API. + + status : typing.Optional[UpdateContentImportSourceRequestStatus] + The status of the content import source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ContentImportSource] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"ai/content_import_sources/{jsonable_encoder(id)}", + method="PUT", + json={ + "sync_behavior": sync_behavior, + "status": status, + "url": url, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ContentImportSource, + construct_type( + type_=ContentImportSource, # 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_content_import_source( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + You can delete a content import source by making a DELETE request this endpoint. This will also delete all external pages that were imported from this source. + + Parameters + ---------- + id : str + The unique identifier for the content import source which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + f"ai/content_import_sources/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + 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_external_pages( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ExternalPagesList]: + """ + You can retrieve a list of all external pages for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ExternalPagesList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "ai/external_pages", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ExternalPagesList, + construct_type( + type_=ExternalPagesList, # 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 create_external_page( + self, + *, + title: str, + html: str, + source_id: int, + external_id: str, + url: typing.Optional[str] = OMIT, + ai_agent_availability: typing.Optional[bool] = OMIT, + ai_copilot_availability: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ExternalPage]: + """ + You can create a new external page by sending a POST request to this endpoint. If an external page already exists with the specified source_id and external_id, it will be updated instead. + + Parameters + ---------- + title : str + The title of the external page. + + html : str + The body of the external page in HTML. + + source_id : int + The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + + external_id : str + The identifier for the external page which was given by the source. Must be unique for the source. + + url : typing.Optional[str] + The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. When a URL is not present, Fin will not reference the source. + + ai_agent_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by AI Agent. Will not default when updating an existing external page. + + ai_copilot_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by AI Copilot. Will not default when updating an existing external page. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ExternalPage] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "ai/external_pages", + method="POST", + json={ + "title": title, + "html": html, + "url": url, + "ai_agent_availability": ai_agent_availability, + "ai_copilot_availability": ai_copilot_availability, + "source_id": source_id, + "external_id": external_id, + "locale": "en", + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ExternalPage, + construct_type( + type_=ExternalPage, # 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 get_external_page( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ExternalPage]: + """ + You can retrieve an external page. + + Parameters + ---------- + id : str + The unique identifier for the external page which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ExternalPage] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"ai/external_pages/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ExternalPage, + construct_type( + type_=ExternalPage, # 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_external_page( + self, + id: str, + *, + title: str, + html: str, + url: str, + source_id: int, + fin_availability: typing.Optional[bool] = OMIT, + external_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ExternalPage]: + """ + You can update an existing external page (if it was created via the API). + + Parameters + ---------- + id : str + The unique identifier for the external page which is given by Intercom. + + title : str + The title of the external page. + + html : str + The body of the external page in HTML. + + url : str + The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. + + source_id : int + The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + + fin_availability : typing.Optional[bool] + Whether the external page should be used to answer questions by Fin. + + external_id : typing.Optional[str] + The identifier for the external page which was given by the source. Must be unique for the source. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ExternalPage] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"ai/external_pages/{jsonable_encoder(id)}", + method="PUT", + json={ + "title": title, + "html": html, + "url": url, + "fin_availability": fin_availability, + "source_id": source_id, + "external_id": external_id, + "locale": "en", + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ExternalPage, + construct_type( + type_=ExternalPage, # 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_external_page( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ExternalPage]: + """ + Sending a DELETE request for an external page will remove it from the content library UI and from being used for AI answers. + + Parameters + ---------- + id : str + The unique identifier for the external page which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ExternalPage] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"ai/external_pages/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ExternalPage, + construct_type( + type_=ExternalPage, # 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) diff --git a/src/intercom/unstable/ai_content/types/__init__.py b/src/intercom/unstable/ai_content/types/__init__.py new file mode 100644 index 00000000..59fd64a1 --- /dev/null +++ b/src/intercom/unstable/ai_content/types/__init__.py @@ -0,0 +1,62 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .content_import_source import ContentImportSource + from .content_import_source_status import ContentImportSourceStatus + from .content_import_source_sync_behavior import ContentImportSourceSyncBehavior + from .content_import_sources_list import ContentImportSourcesList + from .create_content_import_source_request_status import CreateContentImportSourceRequestStatus + from .external_page import ExternalPage + from .external_pages_list import ExternalPagesList + from .update_content_import_source_request_status import UpdateContentImportSourceRequestStatus + from .update_content_import_source_request_sync_behavior import UpdateContentImportSourceRequestSyncBehavior +_dynamic_imports: typing.Dict[str, str] = { + "ContentImportSource": ".content_import_source", + "ContentImportSourceStatus": ".content_import_source_status", + "ContentImportSourceSyncBehavior": ".content_import_source_sync_behavior", + "ContentImportSourcesList": ".content_import_sources_list", + "CreateContentImportSourceRequestStatus": ".create_content_import_source_request_status", + "ExternalPage": ".external_page", + "ExternalPagesList": ".external_pages_list", + "UpdateContentImportSourceRequestStatus": ".update_content_import_source_request_status", + "UpdateContentImportSourceRequestSyncBehavior": ".update_content_import_source_request_sync_behavior", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "ContentImportSource", + "ContentImportSourceStatus", + "ContentImportSourceSyncBehavior", + "ContentImportSourcesList", + "CreateContentImportSourceRequestStatus", + "ExternalPage", + "ExternalPagesList", + "UpdateContentImportSourceRequestStatus", + "UpdateContentImportSourceRequestSyncBehavior", +] diff --git a/src/intercom/unstable/ai_content/types/content_import_source.py b/src/intercom/unstable/ai_content/types/content_import_source.py new file mode 100644 index 00000000..06ddde3d --- /dev/null +++ b/src/intercom/unstable/ai_content/types/content_import_source.py @@ -0,0 +1,64 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .content_import_source_status import ContentImportSourceStatus +from .content_import_source_sync_behavior import ContentImportSourceSyncBehavior + + +class ContentImportSource(UncheckedBaseModel): + """ + An external source for External Pages that you add to your Fin Content Library. + """ + + type: typing.Literal["content_import_source"] = pydantic.Field(default="content_import_source") + """ + Always external_page + """ + + id: int = pydantic.Field() + """ + The unique identifier for the content import source which is given by Intercom. + """ + + last_synced_at: int = pydantic.Field() + """ + The time when the content import source was last synced. + """ + + sync_behavior: ContentImportSourceSyncBehavior = pydantic.Field() + """ + If you intend to create or update External Pages via the API, this should be set to `api`. + """ + + status: ContentImportSourceStatus = pydantic.Field() + """ + The status of the content import source. + """ + + url: str = pydantic.Field() + """ + The URL of the root of the external source. + """ + + created_at: int = pydantic.Field() + """ + The time when the content import source was created. + """ + + updated_at: int = pydantic.Field() + """ + The time when the content import source was last updated. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/ai_content/types/content_import_source_status.py b/src/intercom/unstable/ai_content/types/content_import_source_status.py new file mode 100644 index 00000000..389c3c83 --- /dev/null +++ b/src/intercom/unstable/ai_content/types/content_import_source_status.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ContentImportSourceStatus = typing.Union[typing.Literal["active", "deactivated"], typing.Any] diff --git a/src/intercom/unstable/ai_content/types/content_import_source_sync_behavior.py b/src/intercom/unstable/ai_content/types/content_import_source_sync_behavior.py new file mode 100644 index 00000000..97abd401 --- /dev/null +++ b/src/intercom/unstable/ai_content/types/content_import_source_sync_behavior.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ContentImportSourceSyncBehavior = typing.Union[typing.Literal["api", "automatic", "manual"], typing.Any] diff --git a/src/intercom/unstable/ai_content/types/content_import_sources_list.py b/src/intercom/unstable/ai_content/types/content_import_sources_list.py new file mode 100644 index 00000000..6ca7644c --- /dev/null +++ b/src/intercom/unstable/ai_content/types/content_import_sources_list.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...types.pages_link import PagesLink +from .content_import_source import ContentImportSource + + +class ContentImportSourcesList(UncheckedBaseModel): + """ + This will return a list of the content import sources for the App. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object - `list`. + """ + + pages: typing.Optional[PagesLink] = None + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + A count of the total number of content import sources. + """ + + data: typing.Optional[typing.List[ContentImportSource]] = pydantic.Field(default=None) + """ + An array of Content Import Source objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/ai_content/types/create_content_import_source_request_status.py b/src/intercom/unstable/ai_content/types/create_content_import_source_request_status.py new file mode 100644 index 00000000..047dfd52 --- /dev/null +++ b/src/intercom/unstable/ai_content/types/create_content_import_source_request_status.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CreateContentImportSourceRequestStatus = typing.Union[typing.Literal["active", "deactivated"], typing.Any] diff --git a/src/intercom/unstable/ai_content/types/external_page.py b/src/intercom/unstable/ai_content/types/external_page.py new file mode 100644 index 00000000..7c88b22d --- /dev/null +++ b/src/intercom/unstable/ai_content/types/external_page.py @@ -0,0 +1,92 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class ExternalPage(UncheckedBaseModel): + """ + External pages that you have added to your Fin Content Library. + """ + + type: typing.Literal["external_page"] = pydantic.Field(default="external_page") + """ + Always external_page + """ + + id: str = pydantic.Field() + """ + The unique identifier for the external page which is given by Intercom. + """ + + title: str = pydantic.Field() + """ + The title of the external page. + """ + + html: str = pydantic.Field() + """ + The body of the external page in HTML. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL of the external page. This will be used by Fin to link end users to the page it based its answer on. + """ + + ai_agent_availability: bool = pydantic.Field() + """ + Whether the external page should be used to answer questions by AI Agent. + """ + + ai_copilot_availability: bool = pydantic.Field() + """ + Whether the external page should be used to answer questions by AI Copilot. + """ + + fin_availability: typing.Optional[bool] = pydantic.Field(default=None) + """ + Deprecated. Use ai_agent_availability and ai_copilot_availability instead. + """ + + locale: typing.Literal["en"] = pydantic.Field(default="en") + """ + Always en + """ + + source_id: int = pydantic.Field() + """ + The unique identifier for the source of the external page which was given by Intercom. Every external page must be associated with a Content Import Source which represents the place it comes from and from which it inherits a default audience (configured in the UI). For a new source, make a POST request to the Content Import Source endpoint and an ID for the source will be returned in the response. + """ + + external_id: str = pydantic.Field() + """ + The identifier for the external page which was given by the source. Must be unique for the source. + """ + + created_at: int = pydantic.Field() + """ + The time when the external page was created. + """ + + updated_at: int = pydantic.Field() + """ + The time when the external page was last updated. + """ + + last_ingested_at: int = pydantic.Field() + """ + The time when the external page was last ingested. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/ai_content/types/external_pages_list.py b/src/intercom/unstable/ai_content/types/external_pages_list.py new file mode 100644 index 00000000..b0daed39 --- /dev/null +++ b/src/intercom/unstable/ai_content/types/external_pages_list.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...types.pages_link import PagesLink +from .external_page import ExternalPage + + +class ExternalPagesList(UncheckedBaseModel): + """ + This will return a list of external pages for the App. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object - `list`. + """ + + pages: typing.Optional[PagesLink] = None + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + A count of the total number of external pages. + """ + + data: typing.Optional[typing.List[ExternalPage]] = pydantic.Field(default=None) + """ + An array of External Page objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/ai_content/types/update_content_import_source_request_status.py b/src/intercom/unstable/ai_content/types/update_content_import_source_request_status.py new file mode 100644 index 00000000..080fcef0 --- /dev/null +++ b/src/intercom/unstable/ai_content/types/update_content_import_source_request_status.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +UpdateContentImportSourceRequestStatus = typing.Union[typing.Literal["active", "deactivated"], typing.Any] diff --git a/src/intercom/unstable/ai_content/types/update_content_import_source_request_sync_behavior.py b/src/intercom/unstable/ai_content/types/update_content_import_source_request_sync_behavior.py new file mode 100644 index 00000000..19fc0b06 --- /dev/null +++ b/src/intercom/unstable/ai_content/types/update_content_import_source_request_sync_behavior.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +UpdateContentImportSourceRequestSyncBehavior = typing.Union[typing.Literal["api", "automated", "manual"], typing.Any] diff --git a/src/intercom/unstable/ai_content_source/__init__.py b/src/intercom/unstable/ai_content_source/__init__.py new file mode 100644 index 00000000..82d9ccdb --- /dev/null +++ b/src/intercom/unstable/ai_content_source/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ContentSource, ContentSourceContentType +_dynamic_imports: typing.Dict[str, str] = {"ContentSource": ".types", "ContentSourceContentType": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["ContentSource", "ContentSourceContentType"] diff --git a/src/intercom/unstable/ai_content_source/types/__init__.py b/src/intercom/unstable/ai_content_source/types/__init__.py new file mode 100644 index 00000000..99ea798a --- /dev/null +++ b/src/intercom/unstable/ai_content_source/types/__init__.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .content_source import ContentSource + from .content_source_content_type import ContentSourceContentType +_dynamic_imports: typing.Dict[str, str] = { + "ContentSource": ".content_source", + "ContentSourceContentType": ".content_source_content_type", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["ContentSource", "ContentSourceContentType"] diff --git a/src/intercom/unstable/ai_content_source/types/content_source.py b/src/intercom/unstable/ai_content_source/types/content_source.py new file mode 100644 index 00000000..9adda1fd --- /dev/null +++ b/src/intercom/unstable/ai_content_source/types/content_source.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .content_source_content_type import ContentSourceContentType + + +class ContentSource(UncheckedBaseModel): + """ + The content source used by AI Agent in the conversation. + """ + + content_type: typing.Optional[ContentSourceContentType] = pydantic.Field(default=None) + """ + The type of the content source. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The internal URL linking to the content source for teammates. + """ + + title: typing.Optional[str] = pydantic.Field(default=None) + """ + The title of the content source. + """ + + locale: typing.Optional[str] = pydantic.Field(default=None) + """ + The ISO 639 language code of the content source. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/ai_content_source/types/content_source_content_type.py b/src/intercom/unstable/ai_content_source/types/content_source_content_type.py new file mode 100644 index 00000000..ce555ed3 --- /dev/null +++ b/src/intercom/unstable/ai_content_source/types/content_source_content_type.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ContentSourceContentType = typing.Union[ + typing.Literal["file", "article", "external_content", "content_snippet", "workflow_connector_action"], typing.Any +] diff --git a/src/intercom/unstable/articles/__init__.py b/src/intercom/unstable/articles/__init__.py new file mode 100644 index 00000000..820d701e --- /dev/null +++ b/src/intercom/unstable/articles/__init__.py @@ -0,0 +1,70 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + Article, + ArticleListItem, + ArticleListItemState, + ArticleSearchHighlights, + ArticleSearchHighlightsHighlightedSummaryItemItem, + ArticleSearchHighlightsHighlightedSummaryItemItemType, + ArticleSearchHighlightsHighlightedTitleItem, + ArticleSearchHighlightsHighlightedTitleItemType, + ArticleSearchResponse, + ArticleSearchResponseData, + InternalArticle, + ) +_dynamic_imports: typing.Dict[str, str] = { + "Article": ".types", + "ArticleListItem": ".types", + "ArticleListItemState": ".types", + "ArticleSearchHighlights": ".types", + "ArticleSearchHighlightsHighlightedSummaryItemItem": ".types", + "ArticleSearchHighlightsHighlightedSummaryItemItemType": ".types", + "ArticleSearchHighlightsHighlightedTitleItem": ".types", + "ArticleSearchHighlightsHighlightedTitleItemType": ".types", + "ArticleSearchResponse": ".types", + "ArticleSearchResponseData": ".types", + "InternalArticle": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "Article", + "ArticleListItem", + "ArticleListItemState", + "ArticleSearchHighlights", + "ArticleSearchHighlightsHighlightedSummaryItemItem", + "ArticleSearchHighlightsHighlightedSummaryItemItemType", + "ArticleSearchHighlightsHighlightedTitleItem", + "ArticleSearchHighlightsHighlightedTitleItemType", + "ArticleSearchResponse", + "ArticleSearchResponseData", + "InternalArticle", +] diff --git a/src/intercom/unstable/articles/client.py b/src/intercom/unstable/articles/client.py new file mode 100644 index 00000000..dc2a52b5 --- /dev/null +++ b/src/intercom/unstable/articles/client.py @@ -0,0 +1,452 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..types.article_list import ArticleList +from ..types.deleted_article_object import DeletedArticleObject +from .raw_client import AsyncRawArticlesClient, RawArticlesClient +from .types.article import Article +from .types.article_search_response import ArticleSearchResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class ArticlesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawArticlesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawArticlesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawArticlesClient + """ + return self._raw_client + + def list_articles(self, *, request_options: typing.Optional[RequestOptions] = None) -> ArticleList: + """ + You can fetch a list of all articles by making a GET request to `https://api.intercom.io/articles`. + + > 📘 How are the articles sorted and ordered? + > + > Articles will be returned in descending order on the `updated_at` attribute. This means if you need to iterate through results then we'll show the most recently updated articles first. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ArticleList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.articles.list_articles() + """ + _response = self._raw_client.list_articles(request_options=request_options) + return _response.data + + def create_article( + self, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> Article: + """ + You can create a new article by making a POST request to `https://api.intercom.io/articles`. + + Parameters + ---------- + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Article + article created + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.articles.create_article( + request={"key": "value"}, + ) + """ + _response = self._raw_client.create_article(request=request, request_options=request_options) + return _response.data + + def retrieve_article(self, id: int, *, request_options: typing.Optional[RequestOptions] = None) -> Article: + """ + You can fetch the details of a single article by making a GET request to `https://api.intercom.io/articles/`. + + Parameters + ---------- + id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Article + Article found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.articles.retrieve_article( + id=1, + ) + """ + _response = self._raw_client.retrieve_article(id, request_options=request_options) + return _response.data + + def delete_article( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeletedArticleObject: + """ + You can delete a single article by making a DELETE request to `https://api.intercom.io/articles/`. + + Parameters + ---------- + id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeletedArticleObject + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.articles.delete_article( + id=1, + ) + """ + _response = self._raw_client.delete_article(id, request_options=request_options) + return _response.data + + def search_articles( + self, + *, + phrase: typing.Optional[str] = None, + state: typing.Optional[str] = None, + help_center_id: typing.Optional[int] = None, + highlight: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ArticleSearchResponse: + """ + You can search for articles by making a GET request to `https://api.intercom.io/articles/search`. + + Parameters + ---------- + phrase : typing.Optional[str] + The phrase within your articles to search for. + + state : typing.Optional[str] + The state of the Articles returned. One of `published`, `draft` or `all`. + + help_center_id : typing.Optional[int] + The ID of the Help Center to search in. + + highlight : typing.Optional[bool] + Return a highlighted version of the matching content within your articles. Refer to the response schema for more details. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ArticleSearchResponse + Search successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.articles.search_articles( + phrase="Getting started", + state="published", + help_center_id=1, + highlight=True, + ) + """ + _response = self._raw_client.search_articles( + phrase=phrase, + state=state, + help_center_id=help_center_id, + highlight=highlight, + request_options=request_options, + ) + return _response.data + + +class AsyncArticlesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawArticlesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawArticlesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawArticlesClient + """ + return self._raw_client + + async def list_articles(self, *, request_options: typing.Optional[RequestOptions] = None) -> ArticleList: + """ + You can fetch a list of all articles by making a GET request to `https://api.intercom.io/articles`. + + > 📘 How are the articles sorted and ordered? + > + > Articles will be returned in descending order on the `updated_at` attribute. This means if you need to iterate through results then we'll show the most recently updated articles first. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ArticleList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.articles.list_articles() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_articles(request_options=request_options) + return _response.data + + async def create_article( + self, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> Article: + """ + You can create a new article by making a POST request to `https://api.intercom.io/articles`. + + Parameters + ---------- + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Article + article created + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.articles.create_article( + request={"key": "value"}, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_article(request=request, request_options=request_options) + return _response.data + + async def retrieve_article(self, id: int, *, request_options: typing.Optional[RequestOptions] = None) -> Article: + """ + You can fetch the details of a single article by making a GET request to `https://api.intercom.io/articles/`. + + Parameters + ---------- + id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Article + Article found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.articles.retrieve_article( + id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.retrieve_article(id, request_options=request_options) + return _response.data + + async def delete_article( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeletedArticleObject: + """ + You can delete a single article by making a DELETE request to `https://api.intercom.io/articles/`. + + Parameters + ---------- + id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeletedArticleObject + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.articles.delete_article( + id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_article(id, request_options=request_options) + return _response.data + + async def search_articles( + self, + *, + phrase: typing.Optional[str] = None, + state: typing.Optional[str] = None, + help_center_id: typing.Optional[int] = None, + highlight: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ArticleSearchResponse: + """ + You can search for articles by making a GET request to `https://api.intercom.io/articles/search`. + + Parameters + ---------- + phrase : typing.Optional[str] + The phrase within your articles to search for. + + state : typing.Optional[str] + The state of the Articles returned. One of `published`, `draft` or `all`. + + help_center_id : typing.Optional[int] + The ID of the Help Center to search in. + + highlight : typing.Optional[bool] + Return a highlighted version of the matching content within your articles. Refer to the response schema for more details. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ArticleSearchResponse + Search successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.articles.search_articles( + phrase="Getting started", + state="published", + help_center_id=1, + highlight=True, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.search_articles( + phrase=phrase, + state=state, + help_center_id=help_center_id, + highlight=highlight, + request_options=request_options, + ) + return _response.data diff --git a/src/intercom/unstable/articles/raw_client.py b/src/intercom/unstable/articles/raw_client.py new file mode 100644 index 00000000..babed8d1 --- /dev/null +++ b/src/intercom/unstable/articles/raw_client.py @@ -0,0 +1,648 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.bad_request_error import BadRequestError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.article_list import ArticleList +from ..types.deleted_article_object import DeletedArticleObject +from ..types.error import Error +from .types.article import Article +from .types.article_search_response import ArticleSearchResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawArticlesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_articles(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[ArticleList]: + """ + You can fetch a list of all articles by making a GET request to `https://api.intercom.io/articles`. + + > 📘 How are the articles sorted and ordered? + > + > Articles will be returned in descending order on the `updated_at` attribute. This means if you need to iterate through results then we'll show the most recently updated articles first. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ArticleList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "articles", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ArticleList, + construct_type( + type_=ArticleList, # 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 create_article( + self, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Article]: + """ + You can create a new article by making a POST request to `https://api.intercom.io/articles`. + + Parameters + ---------- + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Article] + article created + """ + _response = self._client_wrapper.httpx_client.request( + "articles", + method="POST", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Article, + construct_type( + type_=Article, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 retrieve_article( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Article]: + """ + You can fetch the details of a single article by making a GET request to `https://api.intercom.io/articles/`. + + Parameters + ---------- + id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Article] + Article found + """ + _response = self._client_wrapper.httpx_client.request( + f"articles/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Article, + construct_type( + type_=Article, # 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 delete_article( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DeletedArticleObject]: + """ + You can delete a single article by making a DELETE request to `https://api.intercom.io/articles/`. + + Parameters + ---------- + id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeletedArticleObject] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"articles/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeletedArticleObject, + construct_type( + type_=DeletedArticleObject, # 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 search_articles( + self, + *, + phrase: typing.Optional[str] = None, + state: typing.Optional[str] = None, + help_center_id: typing.Optional[int] = None, + highlight: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ArticleSearchResponse]: + """ + You can search for articles by making a GET request to `https://api.intercom.io/articles/search`. + + Parameters + ---------- + phrase : typing.Optional[str] + The phrase within your articles to search for. + + state : typing.Optional[str] + The state of the Articles returned. One of `published`, `draft` or `all`. + + help_center_id : typing.Optional[int] + The ID of the Help Center to search in. + + highlight : typing.Optional[bool] + Return a highlighted version of the matching content within your articles. Refer to the response schema for more details. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ArticleSearchResponse] + Search successful + """ + _response = self._client_wrapper.httpx_client.request( + "articles/search", + method="GET", + params={ + "phrase": phrase, + "state": state, + "help_center_id": help_center_id, + "highlight": highlight, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ArticleSearchResponse, + construct_type( + type_=ArticleSearchResponse, # 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) + + +class AsyncRawArticlesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_articles( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ArticleList]: + """ + You can fetch a list of all articles by making a GET request to `https://api.intercom.io/articles`. + + > 📘 How are the articles sorted and ordered? + > + > Articles will be returned in descending order on the `updated_at` attribute. This means if you need to iterate through results then we'll show the most recently updated articles first. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ArticleList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "articles", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ArticleList, + construct_type( + type_=ArticleList, # 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 create_article( + self, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Article]: + """ + You can create a new article by making a POST request to `https://api.intercom.io/articles`. + + Parameters + ---------- + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Article] + article created + """ + _response = await self._client_wrapper.httpx_client.request( + "articles", + method="POST", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Article, + construct_type( + type_=Article, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 retrieve_article( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Article]: + """ + You can fetch the details of a single article by making a GET request to `https://api.intercom.io/articles/`. + + Parameters + ---------- + id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Article] + Article found + """ + _response = await self._client_wrapper.httpx_client.request( + f"articles/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Article, + construct_type( + type_=Article, # 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 delete_article( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DeletedArticleObject]: + """ + You can delete a single article by making a DELETE request to `https://api.intercom.io/articles/`. + + Parameters + ---------- + id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeletedArticleObject] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"articles/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeletedArticleObject, + construct_type( + type_=DeletedArticleObject, # 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 search_articles( + self, + *, + phrase: typing.Optional[str] = None, + state: typing.Optional[str] = None, + help_center_id: typing.Optional[int] = None, + highlight: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ArticleSearchResponse]: + """ + You can search for articles by making a GET request to `https://api.intercom.io/articles/search`. + + Parameters + ---------- + phrase : typing.Optional[str] + The phrase within your articles to search for. + + state : typing.Optional[str] + The state of the Articles returned. One of `published`, `draft` or `all`. + + help_center_id : typing.Optional[int] + The ID of the Help Center to search in. + + highlight : typing.Optional[bool] + Return a highlighted version of the matching content within your articles. Refer to the response schema for more details. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ArticleSearchResponse] + Search successful + """ + _response = await self._client_wrapper.httpx_client.request( + "articles/search", + method="GET", + params={ + "phrase": phrase, + "state": state, + "help_center_id": help_center_id, + "highlight": highlight, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ArticleSearchResponse, + construct_type( + type_=ArticleSearchResponse, # 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) diff --git a/src/intercom/unstable/articles/types/__init__.py b/src/intercom/unstable/articles/types/__init__.py new file mode 100644 index 00000000..f5b2100c --- /dev/null +++ b/src/intercom/unstable/articles/types/__init__.py @@ -0,0 +1,72 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .article import Article + from .article_list_item import ArticleListItem + from .article_list_item_state import ArticleListItemState + from .article_search_highlights import ArticleSearchHighlights + from .article_search_highlights_highlighted_summary_item_item import ( + ArticleSearchHighlightsHighlightedSummaryItemItem, + ) + from .article_search_highlights_highlighted_summary_item_item_type import ( + ArticleSearchHighlightsHighlightedSummaryItemItemType, + ) + from .article_search_highlights_highlighted_title_item import ArticleSearchHighlightsHighlightedTitleItem + from .article_search_highlights_highlighted_title_item_type import ArticleSearchHighlightsHighlightedTitleItemType + from .article_search_response import ArticleSearchResponse + from .article_search_response_data import ArticleSearchResponseData + from .internal_article import InternalArticle +_dynamic_imports: typing.Dict[str, str] = { + "Article": ".article", + "ArticleListItem": ".article_list_item", + "ArticleListItemState": ".article_list_item_state", + "ArticleSearchHighlights": ".article_search_highlights", + "ArticleSearchHighlightsHighlightedSummaryItemItem": ".article_search_highlights_highlighted_summary_item_item", + "ArticleSearchHighlightsHighlightedSummaryItemItemType": ".article_search_highlights_highlighted_summary_item_item_type", + "ArticleSearchHighlightsHighlightedTitleItem": ".article_search_highlights_highlighted_title_item", + "ArticleSearchHighlightsHighlightedTitleItemType": ".article_search_highlights_highlighted_title_item_type", + "ArticleSearchResponse": ".article_search_response", + "ArticleSearchResponseData": ".article_search_response_data", + "InternalArticle": ".internal_article", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "Article", + "ArticleListItem", + "ArticleListItemState", + "ArticleSearchHighlights", + "ArticleSearchHighlightsHighlightedSummaryItemItem", + "ArticleSearchHighlightsHighlightedSummaryItemItemType", + "ArticleSearchHighlightsHighlightedTitleItem", + "ArticleSearchHighlightsHighlightedTitleItemType", + "ArticleSearchResponse", + "ArticleSearchResponseData", + "InternalArticle", +] diff --git a/src/intercom/unstable/articles/types/article.py b/src/intercom/unstable/articles/types/article.py new file mode 100644 index 00000000..5d36cc94 --- /dev/null +++ b/src/intercom/unstable/articles/types/article.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ...types.article_statistics import ArticleStatistics +from .article_list_item import ArticleListItem + + +class Article(ArticleListItem): + """ + The Articles API is a central place to gather all information and take actions on your articles. Articles can live within collections and sections, or alternatively they can stand alone. + """ + + statistics: typing.Optional[ArticleStatistics] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/articles/types/article_list_item.py b/src/intercom/unstable/articles/types/article_list_item.py new file mode 100644 index 00000000..39a385a3 --- /dev/null +++ b/src/intercom/unstable/articles/types/article_list_item.py @@ -0,0 +1,101 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...types.article_translated_content import ArticleTranslatedContent +from .article_list_item_state import ArticleListItemState + + +class ArticleListItem(UncheckedBaseModel): + """ + The data returned about your articles when you list them. + """ + + type: typing.Optional[typing.Literal["article"]] = pydantic.Field(default=None) + """ + The type of object - `article`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the article which is given by Intercom. + """ + + workspace_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the workspace which the article belongs to. + """ + + title: typing.Optional[str] = pydantic.Field(default=None) + """ + The title of the article. For multilingual articles, this will be the title of the default language's content. + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + The description of the article. For multilingual articles, this will be the description of the default language's content. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The body of the article in HTML. For multilingual articles, this will be the body of the default language's content. + """ + + author_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of the author of the article. For multilingual articles, this will be the id of the author of the default language's content. Must be a teammate on the help center's workspace. + """ + + state: typing.Optional[ArticleListItemState] = pydantic.Field(default=None) + """ + Whether the article is `published` or is a `draft`. For multilingual articles, this will be the state of the default language's content. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the article was created. For multilingual articles, this will be the timestamp of creation of the default language's content in seconds. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the article was last updated. For multilingual articles, this will be the timestamp of last update of the default language's content in seconds. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL of the article. For multilingual articles, this will be the URL of the default language's content. + """ + + parent_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of the article's parent collection or section. An article without this field stands alone. + """ + + parent_ids: typing.Optional[typing.List[int]] = pydantic.Field(default=None) + """ + The ids of the article's parent collections or sections. An article without this field stands alone. + """ + + parent_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of parent, which can either be a `collection` or `section`. + """ + + default_locale: typing.Optional[str] = pydantic.Field(default=None) + """ + The default locale of the help center. This field is only returned for multilingual help centers. + """ + + translated_content: typing.Optional[ArticleTranslatedContent] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/articles/types/article_list_item_state.py b/src/intercom/unstable/articles/types/article_list_item_state.py new file mode 100644 index 00000000..013499d9 --- /dev/null +++ b/src/intercom/unstable/articles/types/article_list_item_state.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ArticleListItemState = typing.Union[typing.Literal["published", "draft"], typing.Any] diff --git a/src/intercom/unstable/articles/types/article_search_highlights.py b/src/intercom/unstable/articles/types/article_search_highlights.py new file mode 100644 index 00000000..f64a189e --- /dev/null +++ b/src/intercom/unstable/articles/types/article_search_highlights.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .article_search_highlights_highlighted_summary_item_item import ArticleSearchHighlightsHighlightedSummaryItemItem +from .article_search_highlights_highlighted_title_item import ArticleSearchHighlightsHighlightedTitleItem + + +class ArticleSearchHighlights(UncheckedBaseModel): + """ + The highlighted results of an Article search. In the examples provided my search query is always "my query". + """ + + article_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the corresponding article. + """ + + highlighted_title: typing.Optional[typing.List[ArticleSearchHighlightsHighlightedTitleItem]] = pydantic.Field( + default=None + ) + """ + An Article title highlighted. + """ + + highlighted_summary: typing.Optional[ + typing.List[typing.List[ArticleSearchHighlightsHighlightedSummaryItemItem]] + ] = pydantic.Field(default=None) + """ + An Article description and body text highlighted. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/articles/types/article_search_highlights_highlighted_summary_item_item.py b/src/intercom/unstable/articles/types/article_search_highlights_highlighted_summary_item_item.py new file mode 100644 index 00000000..fd6e82a2 --- /dev/null +++ b/src/intercom/unstable/articles/types/article_search_highlights_highlighted_summary_item_item.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .article_search_highlights_highlighted_summary_item_item_type import ( + ArticleSearchHighlightsHighlightedSummaryItemItemType, +) + + +class ArticleSearchHighlightsHighlightedSummaryItemItem(UncheckedBaseModel): + """ + An instance of highlighted summary text. + """ + + type: typing.Optional[ArticleSearchHighlightsHighlightedSummaryItemItemType] = pydantic.Field(default=None) + """ + The type of text - `highlight` or `plain`. + """ + + text: typing.Optional[str] = pydantic.Field(default=None) + """ + The text of the title. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/articles/types/article_search_highlights_highlighted_summary_item_item_type.py b/src/intercom/unstable/articles/types/article_search_highlights_highlighted_summary_item_item_type.py new file mode 100644 index 00000000..295a1ec2 --- /dev/null +++ b/src/intercom/unstable/articles/types/article_search_highlights_highlighted_summary_item_item_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ArticleSearchHighlightsHighlightedSummaryItemItemType = typing.Union[typing.Literal["highlight", "plain"], typing.Any] diff --git a/src/intercom/unstable/articles/types/article_search_highlights_highlighted_title_item.py b/src/intercom/unstable/articles/types/article_search_highlights_highlighted_title_item.py new file mode 100644 index 00000000..a9dddec2 --- /dev/null +++ b/src/intercom/unstable/articles/types/article_search_highlights_highlighted_title_item.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .article_search_highlights_highlighted_title_item_type import ArticleSearchHighlightsHighlightedTitleItemType + + +class ArticleSearchHighlightsHighlightedTitleItem(UncheckedBaseModel): + """ + A highlighted article title. + """ + + type: typing.Optional[ArticleSearchHighlightsHighlightedTitleItemType] = pydantic.Field(default=None) + """ + The type of text - `highlight` or `plain`. + """ + + text: typing.Optional[str] = pydantic.Field(default=None) + """ + The text of the title. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/articles/types/article_search_highlights_highlighted_title_item_type.py b/src/intercom/unstable/articles/types/article_search_highlights_highlighted_title_item_type.py new file mode 100644 index 00000000..45d4316b --- /dev/null +++ b/src/intercom/unstable/articles/types/article_search_highlights_highlighted_title_item_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ArticleSearchHighlightsHighlightedTitleItemType = typing.Union[typing.Literal["highlight", "plain"], typing.Any] diff --git a/src/intercom/unstable/articles/types/article_search_response.py b/src/intercom/unstable/articles/types/article_search_response.py new file mode 100644 index 00000000..8459f4e3 --- /dev/null +++ b/src/intercom/unstable/articles/types/article_search_response.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...types.cursor_pages import CursorPages +from .article_search_response_data import ArticleSearchResponseData + + +class ArticleSearchResponse(UncheckedBaseModel): + """ + The results of an Article search + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object - `list`. + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + The total number of Articles matching the search query + """ + + data: typing.Optional[ArticleSearchResponseData] = pydantic.Field(default=None) + """ + An object containing the results of the search. + """ + + pages: typing.Optional[CursorPages] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/articles/types/article_search_response_data.py b/src/intercom/unstable/articles/types/article_search_response_data.py new file mode 100644 index 00000000..9312b1e6 --- /dev/null +++ b/src/intercom/unstable/articles/types/article_search_response_data.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .article import Article +from .article_search_highlights import ArticleSearchHighlights + + +class ArticleSearchResponseData(UncheckedBaseModel): + """ + An object containing the results of the search. + """ + + articles: typing.Optional[typing.List[Article]] = pydantic.Field(default=None) + """ + An array of Article objects + """ + + highlights: typing.Optional[typing.List[ArticleSearchHighlights]] = pydantic.Field(default=None) + """ + A corresponding array of highlighted Article content + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/articles/types/internal_article.py b/src/intercom/unstable/articles/types/internal_article.py new file mode 100644 index 00000000..3b83a547 --- /dev/null +++ b/src/intercom/unstable/articles/types/internal_article.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +from ...internal_articles.types.internal_article_list_item import InternalArticleListItem + +InternalArticle = InternalArticleListItem diff --git a/src/intercom/unstable/away_status_reasons/__init__.py b/src/intercom/unstable/away_status_reasons/__init__.py new file mode 100644 index 00000000..5cde0202 --- /dev/null +++ b/src/intercom/unstable/away_status_reasons/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/src/intercom/unstable/away_status_reasons/client.py b/src/intercom/unstable/away_status_reasons/client.py new file mode 100644 index 00000000..c6cd9606 --- /dev/null +++ b/src/intercom/unstable/away_status_reasons/client.py @@ -0,0 +1,104 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..types.away_status_reason import AwayStatusReason +from .raw_client import AsyncRawAwayStatusReasonsClient, RawAwayStatusReasonsClient + + +class AwayStatusReasonsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawAwayStatusReasonsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawAwayStatusReasonsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawAwayStatusReasonsClient + """ + return self._raw_client + + def list_away_status_reasons( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.List[AwayStatusReason]: + """ + Returns a list of all away status reasons configured for the workspace, including deleted ones. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.List[AwayStatusReason] + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.away_status_reasons.list_away_status_reasons() + """ + _response = self._raw_client.list_away_status_reasons(request_options=request_options) + return _response.data + + +class AsyncAwayStatusReasonsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawAwayStatusReasonsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawAwayStatusReasonsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawAwayStatusReasonsClient + """ + return self._raw_client + + async def list_away_status_reasons( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.List[AwayStatusReason]: + """ + Returns a list of all away status reasons configured for the workspace, including deleted ones. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.List[AwayStatusReason] + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.away_status_reasons.list_away_status_reasons() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_away_status_reasons(request_options=request_options) + return _response.data diff --git a/src/intercom/unstable/away_status_reasons/raw_client.py b/src/intercom/unstable/away_status_reasons/raw_client.py new file mode 100644 index 00000000..0bdd6c73 --- /dev/null +++ b/src/intercom/unstable/away_status_reasons/raw_client.py @@ -0,0 +1,117 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.http_response import AsyncHttpResponse, HttpResponse +from ...core.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.unauthorized_error import UnauthorizedError +from ..types.away_status_reason import AwayStatusReason +from ..types.error import Error + + +class RawAwayStatusReasonsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_away_status_reasons( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[typing.List[AwayStatusReason]]: + """ + Returns a list of all away status reasons configured for the workspace, including deleted ones. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.List[AwayStatusReason]] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "away_status_reasons", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.List[AwayStatusReason], + construct_type( + type_=typing.List[AwayStatusReason], # 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) + + +class AsyncRawAwayStatusReasonsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_away_status_reasons( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[typing.List[AwayStatusReason]]: + """ + Returns a list of all away status reasons configured for the workspace, including deleted ones. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.List[AwayStatusReason]] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "away_status_reasons", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.List[AwayStatusReason], + construct_type( + type_=typing.List[AwayStatusReason], # 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) diff --git a/src/intercom/unstable/brands/__init__.py b/src/intercom/unstable/brands/__init__.py new file mode 100644 index 00000000..abd83634 --- /dev/null +++ b/src/intercom/unstable/brands/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import Brand, BrandList +_dynamic_imports: typing.Dict[str, str] = {"Brand": ".types", "BrandList": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Brand", "BrandList"] diff --git a/src/intercom/unstable/brands/client.py b/src/intercom/unstable/brands/client.py new file mode 100644 index 00000000..d9879b45 --- /dev/null +++ b/src/intercom/unstable/brands/client.py @@ -0,0 +1,173 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from .raw_client import AsyncRawBrandsClient, RawBrandsClient +from .types.brand import Brand +from .types.brand_list import BrandList + + +class BrandsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawBrandsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawBrandsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawBrandsClient + """ + return self._raw_client + + def list_brands(self, *, request_options: typing.Optional[RequestOptions] = None) -> BrandList: + """ + Retrieves all brands for the workspace, including the default brand. + The default brand id always matches the workspace + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + BrandList + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.brands.list_brands() + """ + _response = self._raw_client.list_brands(request_options=request_options) + return _response.data + + def retrieve_brand(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Brand: + """ + Fetches a specific brand by its unique identifier + + Parameters + ---------- + id : str + The unique identifier of the brand + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Brand + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.brands.retrieve_brand( + id="id", + ) + """ + _response = self._raw_client.retrieve_brand(id, request_options=request_options) + return _response.data + + +class AsyncBrandsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawBrandsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawBrandsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawBrandsClient + """ + return self._raw_client + + async def list_brands(self, *, request_options: typing.Optional[RequestOptions] = None) -> BrandList: + """ + Retrieves all brands for the workspace, including the default brand. + The default brand id always matches the workspace + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + BrandList + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.brands.list_brands() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_brands(request_options=request_options) + return _response.data + + async def retrieve_brand(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Brand: + """ + Fetches a specific brand by its unique identifier + + Parameters + ---------- + id : str + The unique identifier of the brand + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Brand + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.brands.retrieve_brand( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.retrieve_brand(id, request_options=request_options) + return _response.data diff --git a/src/intercom/unstable/brands/raw_client.py b/src/intercom/unstable/brands/raw_client.py new file mode 100644 index 00000000..3a8eba18 --- /dev/null +++ b/src/intercom/unstable/brands/raw_client.py @@ -0,0 +1,242 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from .types.brand import Brand +from .types.brand_list import BrandList + + +class RawBrandsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_brands(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[BrandList]: + """ + Retrieves all brands for the workspace, including the default brand. + The default brand id always matches the workspace + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[BrandList] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "brands", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + BrandList, + construct_type( + type_=BrandList, # 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 retrieve_brand( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Brand]: + """ + Fetches a specific brand by its unique identifier + + Parameters + ---------- + id : str + The unique identifier of the brand + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Brand] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + f"brands/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Brand, + construct_type( + type_=Brand, # 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) + + +class AsyncRawBrandsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_brands( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[BrandList]: + """ + Retrieves all brands for the workspace, including the default brand. + The default brand id always matches the workspace + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[BrandList] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "brands", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + BrandList, + construct_type( + type_=BrandList, # 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 retrieve_brand( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Brand]: + """ + Fetches a specific brand by its unique identifier + + Parameters + ---------- + id : str + The unique identifier of the brand + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Brand] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + f"brands/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Brand, + construct_type( + type_=Brand, # 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) diff --git a/src/intercom/unstable/brands/types/__init__.py b/src/intercom/unstable/brands/types/__init__.py new file mode 100644 index 00000000..81623ba7 --- /dev/null +++ b/src/intercom/unstable/brands/types/__init__.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .brand import Brand + from .brand_list import BrandList +_dynamic_imports: typing.Dict[str, str] = {"Brand": ".brand", "BrandList": ".brand_list"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Brand", "BrandList"] diff --git a/src/intercom/unstable/brands/types/brand.py b/src/intercom/unstable/brands/types/brand.py new file mode 100644 index 00000000..8888094f --- /dev/null +++ b/src/intercom/unstable/brands/types/brand.py @@ -0,0 +1,62 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class Brand(UncheckedBaseModel): + """ + Represents a branding configuration for the workspace + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of object + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + Unique brand identifier. For default brand, matches the workspace ID + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Display name of the brand + """ + + is_default: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether this is the workspace's default brand + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Unix timestamp of brand creation + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Unix timestamp of last modification + """ + + help_center_id: typing.Optional[str] = pydantic.Field(default=None) + """ + Associated help center identifier + """ + + default_address_settings_id: typing.Optional[str] = pydantic.Field(default=None) + """ + Default email settings ID for this brand + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/brands/types/brand_list.py b/src/intercom/unstable/brands/types/brand_list.py new file mode 100644 index 00000000..b77a7ce8 --- /dev/null +++ b/src/intercom/unstable/brands/types/brand_list.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .brand import Brand + + +class BrandList(UncheckedBaseModel): + """ + A list of brands + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of object + """ + + data: typing.Optional[typing.List[Brand]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/calls/__init__.py b/src/intercom/unstable/calls/__init__.py new file mode 100644 index 00000000..c3418558 --- /dev/null +++ b/src/intercom/unstable/calls/__init__.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import Call, ListCallsWithTranscriptsResponse, ListCallsWithTranscriptsResponseDataItem +_dynamic_imports: typing.Dict[str, str] = { + "Call": ".types", + "ListCallsWithTranscriptsResponse": ".types", + "ListCallsWithTranscriptsResponseDataItem": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Call", "ListCallsWithTranscriptsResponse", "ListCallsWithTranscriptsResponseDataItem"] diff --git a/src/intercom/unstable/calls/client.py b/src/intercom/unstable/calls/client.py new file mode 100644 index 00000000..1718eb00 --- /dev/null +++ b/src/intercom/unstable/calls/client.py @@ -0,0 +1,756 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..types.ai_call_response import AiCallResponse +from ..types.call_list import CallList +from ..types.error import Error +from ..types.register_fin_voice_call_request import RegisterFinVoiceCallRequest +from .raw_client import AsyncRawCallsClient, RawCallsClient +from .types.call import Call +from .types.list_calls_with_transcripts_response import ListCallsWithTranscriptsResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class CallsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawCallsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawCallsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawCallsClient + """ + return self._raw_client + + def list_calls( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> CallList: + """ + Retrieve a paginated list of calls. + + 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 25. Max 25. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CallList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.calls.list_calls( + page=1, + per_page=1, + ) + """ + _response = self._raw_client.list_calls(page=page, per_page=per_page, request_options=request_options) + return _response.data + + def show_call(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Call: + """ + Retrieve a single call by id. + + Parameters + ---------- + id : str + The id of the call to retrieve + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Call + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.calls.show_call( + id="id", + ) + """ + _response = self._raw_client.show_call(id, request_options=request_options) + return _response.data + + def show_call_recording(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + Redirects to a signed URL for the call's recording if it exists. + + Parameters + ---------- + id : str + The id of the call + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.calls.show_call_recording( + id="id", + ) + """ + _response = self._raw_client.show_call_recording(id, request_options=request_options) + return _response.data + + def show_call_transcript(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> str: + """ + Returns the transcript for the specified call as a downloadable text file. + + Parameters + ---------- + id : str + The id of the call + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + str + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.calls.show_call_transcript( + id="id", + ) + """ + _response = self._raw_client.show_call_transcript(id, request_options=request_options) + return _response.data + + def list_calls_with_transcripts( + self, *, conversation_ids: typing.Sequence[str], request_options: typing.Optional[RequestOptions] = None + ) -> ListCallsWithTranscriptsResponse: + """ + Retrieve calls by a list of conversation ids and include transcripts when available. + A maximum of 20 `conversation_ids` can be provided. If none are provided or more than 20 are provided, a 400 error is returned. + + Parameters + ---------- + conversation_ids : typing.Sequence[str] + A list of conversation ids to fetch calls for. Maximum 20. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ListCallsWithTranscriptsResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.calls.list_calls_with_transcripts( + conversation_ids=["64619700005694", "64619700005695"], + ) + """ + _response = self._raw_client.list_calls_with_transcripts( + conversation_ids=conversation_ids, request_options=request_options + ) + return _response.data + + def register_fin_voice_call( + self, + *, + request: typing.Optional[RegisterFinVoiceCallRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AiCallResponse: + """ + Register a Fin Voice call with Intercom. This endpoint creates an external reference + that links an external call identifier to an Intercom call and conversation. + + The call can be from different sources: + - AWS Connect (default) + - Five9 + - Zoom Phone + + Parameters + ---------- + request : typing.Optional[RegisterFinVoiceCallRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AiCallResponse + successful + + Examples + -------- + from intercom import Intercom + from intercom.unstable import RegisterFinVoiceCallRequest + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.calls.register_fin_voice_call( + request=RegisterFinVoiceCallRequest( + phone_number="+1234567890", + call_id="call-123-abc", + ), + ) + """ + _response = self._raw_client.register_fin_voice_call(request=request, request_options=request_options) + return _response.data + + def collect_fin_voice_call_by_id( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AiCallResponse: + """ + Retrieve information about a Fin Voice call using the external reference ID. + + Parameters + ---------- + id : int + The external reference ID + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AiCallResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.calls.collect_fin_voice_call_by_id( + id=1, + ) + """ + _response = self._raw_client.collect_fin_voice_call_by_id(id, request_options=request_options) + return _response.data + + def collect_fin_voice_call_by_external_id( + self, external_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AiCallResponse: + """ + Retrieve information about a Fin Voice call using the external call identifier. + + Parameters + ---------- + external_id : str + The external call identifier from the call provider + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AiCallResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.calls.collect_fin_voice_call_by_external_id( + external_id="external_id", + ) + """ + _response = self._raw_client.collect_fin_voice_call_by_external_id(external_id, request_options=request_options) + return _response.data + + def collect_fin_voice_call_by_phone_number( + self, phone_number: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Error: + """ + Retrieve information about a Fin Voice call using the phone number. + + Returns the most recent matched call for the given phone number, ordered by creation date. + + Parameters + ---------- + phone_number : str + Phone number in E.164 format + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Error + Unexpected error + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.calls.collect_fin_voice_call_by_phone_number( + phone_number="phone_number", + ) + """ + _response = self._raw_client.collect_fin_voice_call_by_phone_number( + phone_number, request_options=request_options + ) + return _response.data + + +class AsyncCallsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawCallsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawCallsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawCallsClient + """ + return self._raw_client + + async def list_calls( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> CallList: + """ + Retrieve a paginated list of calls. + + 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 25. Max 25. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CallList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.calls.list_calls( + page=1, + per_page=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_calls(page=page, per_page=per_page, request_options=request_options) + return _response.data + + async def show_call(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Call: + """ + Retrieve a single call by id. + + Parameters + ---------- + id : str + The id of the call to retrieve + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Call + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.calls.show_call( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.show_call(id, request_options=request_options) + return _response.data + + async def show_call_recording(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + Redirects to a signed URL for the call's recording if it exists. + + Parameters + ---------- + id : str + The id of the call + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.calls.show_call_recording( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.show_call_recording(id, request_options=request_options) + return _response.data + + async def show_call_transcript(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> str: + """ + Returns the transcript for the specified call as a downloadable text file. + + Parameters + ---------- + id : str + The id of the call + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + str + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.calls.show_call_transcript( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.show_call_transcript(id, request_options=request_options) + return _response.data + + async def list_calls_with_transcripts( + self, *, conversation_ids: typing.Sequence[str], request_options: typing.Optional[RequestOptions] = None + ) -> ListCallsWithTranscriptsResponse: + """ + Retrieve calls by a list of conversation ids and include transcripts when available. + A maximum of 20 `conversation_ids` can be provided. If none are provided or more than 20 are provided, a 400 error is returned. + + Parameters + ---------- + conversation_ids : typing.Sequence[str] + A list of conversation ids to fetch calls for. Maximum 20. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ListCallsWithTranscriptsResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.calls.list_calls_with_transcripts( + conversation_ids=["64619700005694", "64619700005695"], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_calls_with_transcripts( + conversation_ids=conversation_ids, request_options=request_options + ) + return _response.data + + async def register_fin_voice_call( + self, + *, + request: typing.Optional[RegisterFinVoiceCallRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AiCallResponse: + """ + Register a Fin Voice call with Intercom. This endpoint creates an external reference + that links an external call identifier to an Intercom call and conversation. + + The call can be from different sources: + - AWS Connect (default) + - Five9 + - Zoom Phone + + Parameters + ---------- + request : typing.Optional[RegisterFinVoiceCallRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AiCallResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.unstable import RegisterFinVoiceCallRequest + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.calls.register_fin_voice_call( + request=RegisterFinVoiceCallRequest( + phone_number="+1234567890", + call_id="call-123-abc", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.register_fin_voice_call(request=request, request_options=request_options) + return _response.data + + async def collect_fin_voice_call_by_id( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AiCallResponse: + """ + Retrieve information about a Fin Voice call using the external reference ID. + + Parameters + ---------- + id : int + The external reference ID + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AiCallResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.calls.collect_fin_voice_call_by_id( + id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.collect_fin_voice_call_by_id(id, request_options=request_options) + return _response.data + + async def collect_fin_voice_call_by_external_id( + self, external_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AiCallResponse: + """ + Retrieve information about a Fin Voice call using the external call identifier. + + Parameters + ---------- + external_id : str + The external call identifier from the call provider + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AiCallResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.calls.collect_fin_voice_call_by_external_id( + external_id="external_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.collect_fin_voice_call_by_external_id( + external_id, request_options=request_options + ) + return _response.data + + async def collect_fin_voice_call_by_phone_number( + self, phone_number: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Error: + """ + Retrieve information about a Fin Voice call using the phone number. + + Returns the most recent matched call for the given phone number, ordered by creation date. + + Parameters + ---------- + phone_number : str + Phone number in E.164 format + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Error + Unexpected error + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.calls.collect_fin_voice_call_by_phone_number( + phone_number="phone_number", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.collect_fin_voice_call_by_phone_number( + phone_number, request_options=request_options + ) + return _response.data diff --git a/src/intercom/unstable/calls/raw_client.py b/src/intercom/unstable/calls/raw_client.py new file mode 100644 index 00000000..9ea7aa84 --- /dev/null +++ b/src/intercom/unstable/calls/raw_client.py @@ -0,0 +1,1085 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.serialization import convert_and_respect_annotation_metadata +from ...core.unchecked_base_model import construct_type +from ..errors.bad_request_error import BadRequestError +from ..errors.conflict_error import ConflictError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.ai_call_response import AiCallResponse +from ..types.call_list import CallList +from ..types.error import Error +from ..types.register_fin_voice_call_request import RegisterFinVoiceCallRequest +from .types.call import Call +from .types.list_calls_with_transcripts_response import ListCallsWithTranscriptsResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawCallsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_calls( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[CallList]: + """ + Retrieve a paginated list of calls. + + 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 25. Max 25. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CallList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "calls", + method="GET", + params={ + "page": page, + "per_page": per_page, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CallList, + construct_type( + type_=CallList, # 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_call(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Call]: + """ + Retrieve a single call by id. + + Parameters + ---------- + id : str + The id of the call to retrieve + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Call] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"calls/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Call, + construct_type( + type_=Call, # 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 show_call_recording( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[None]: + """ + Redirects to a signed URL for the call's recording if it exists. + + Parameters + ---------- + id : str + The id of the call + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + f"calls/{jsonable_encoder(id)}/recording", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + 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 show_call_transcript( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[str]: + """ + Returns the transcript for the specified call as a downloadable text file. + + Parameters + ---------- + id : str + The id of the call + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[str] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"calls/{jsonable_encoder(id)}/transcript", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=_response.text) # type: ignore + 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_calls_with_transcripts( + self, *, conversation_ids: typing.Sequence[str], request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ListCallsWithTranscriptsResponse]: + """ + Retrieve calls by a list of conversation ids and include transcripts when available. + A maximum of 20 `conversation_ids` can be provided. If none are provided or more than 20 are provided, a 400 error is returned. + + Parameters + ---------- + conversation_ids : typing.Sequence[str] + A list of conversation ids to fetch calls for. Maximum 20. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ListCallsWithTranscriptsResponse] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "calls/search", + method="POST", + json={ + "conversation_ids": conversation_ids, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ListCallsWithTranscriptsResponse, + construct_type( + type_=ListCallsWithTranscriptsResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 register_fin_voice_call( + self, + *, + request: typing.Optional[RegisterFinVoiceCallRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[AiCallResponse]: + """ + Register a Fin Voice call with Intercom. This endpoint creates an external reference + that links an external call identifier to an Intercom call and conversation. + + The call can be from different sources: + - AWS Connect (default) + - Five9 + - Zoom Phone + + Parameters + ---------- + request : typing.Optional[RegisterFinVoiceCallRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[AiCallResponse] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "fin_voice/register", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=RegisterFinVoiceCallRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AiCallResponse, + construct_type( + type_=AiCallResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 409: + raise ConflictError( + 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 collect_fin_voice_call_by_id( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[AiCallResponse]: + """ + Retrieve information about a Fin Voice call using the external reference ID. + + Parameters + ---------- + id : int + The external reference ID + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[AiCallResponse] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"fin_voice/collect/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AiCallResponse, + construct_type( + type_=AiCallResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + 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 collect_fin_voice_call_by_external_id( + self, external_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[AiCallResponse]: + """ + Retrieve information about a Fin Voice call using the external call identifier. + + Parameters + ---------- + external_id : str + The external call identifier from the call provider + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[AiCallResponse] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"fin_voice/external_id/{jsonable_encoder(external_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AiCallResponse, + construct_type( + type_=AiCallResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + 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 collect_fin_voice_call_by_phone_number( + self, phone_number: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Error]: + """ + Retrieve information about a Fin Voice call using the phone number. + + Returns the most recent matched call for the given phone number, ordered by creation date. + + Parameters + ---------- + phone_number : str + Phone number in E.164 format + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Error] + Unexpected error + """ + _response = self._client_wrapper.httpx_client.request( + f"fin_voice/phone_number/{jsonable_encoder(phone_number)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Error, + construct_type( + type_=Error, # 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) + + +class AsyncRawCallsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_calls( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[CallList]: + """ + Retrieve a paginated list of calls. + + 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 25. Max 25. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CallList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "calls", + method="GET", + params={ + "page": page, + "per_page": per_page, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CallList, + construct_type( + type_=CallList, # 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_call( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Call]: + """ + Retrieve a single call by id. + + Parameters + ---------- + id : str + The id of the call to retrieve + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Call] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"calls/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Call, + construct_type( + type_=Call, # 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 show_call_recording( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + Redirects to a signed URL for the call's recording if it exists. + + Parameters + ---------- + id : str + The id of the call + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + f"calls/{jsonable_encoder(id)}/recording", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + 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 show_call_transcript( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[str]: + """ + Returns the transcript for the specified call as a downloadable text file. + + Parameters + ---------- + id : str + The id of the call + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[str] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"calls/{jsonable_encoder(id)}/transcript", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=_response.text) # type: ignore + 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_calls_with_transcripts( + self, *, conversation_ids: typing.Sequence[str], request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ListCallsWithTranscriptsResponse]: + """ + Retrieve calls by a list of conversation ids and include transcripts when available. + A maximum of 20 `conversation_ids` can be provided. If none are provided or more than 20 are provided, a 400 error is returned. + + Parameters + ---------- + conversation_ids : typing.Sequence[str] + A list of conversation ids to fetch calls for. Maximum 20. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ListCallsWithTranscriptsResponse] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "calls/search", + method="POST", + json={ + "conversation_ids": conversation_ids, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ListCallsWithTranscriptsResponse, + construct_type( + type_=ListCallsWithTranscriptsResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 register_fin_voice_call( + self, + *, + request: typing.Optional[RegisterFinVoiceCallRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[AiCallResponse]: + """ + Register a Fin Voice call with Intercom. This endpoint creates an external reference + that links an external call identifier to an Intercom call and conversation. + + The call can be from different sources: + - AWS Connect (default) + - Five9 + - Zoom Phone + + Parameters + ---------- + request : typing.Optional[RegisterFinVoiceCallRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[AiCallResponse] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "fin_voice/register", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=RegisterFinVoiceCallRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AiCallResponse, + construct_type( + type_=AiCallResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 409: + raise ConflictError( + 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 collect_fin_voice_call_by_id( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[AiCallResponse]: + """ + Retrieve information about a Fin Voice call using the external reference ID. + + Parameters + ---------- + id : int + The external reference ID + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[AiCallResponse] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"fin_voice/collect/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AiCallResponse, + construct_type( + type_=AiCallResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + 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 collect_fin_voice_call_by_external_id( + self, external_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[AiCallResponse]: + """ + Retrieve information about a Fin Voice call using the external call identifier. + + Parameters + ---------- + external_id : str + The external call identifier from the call provider + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[AiCallResponse] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"fin_voice/external_id/{jsonable_encoder(external_id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + AiCallResponse, + construct_type( + type_=AiCallResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + 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 collect_fin_voice_call_by_phone_number( + self, phone_number: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Error]: + """ + Retrieve information about a Fin Voice call using the phone number. + + Returns the most recent matched call for the given phone number, ordered by creation date. + + Parameters + ---------- + phone_number : str + Phone number in E.164 format + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Error] + Unexpected error + """ + _response = await self._client_wrapper.httpx_client.request( + f"fin_voice/phone_number/{jsonable_encoder(phone_number)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Error, + construct_type( + type_=Error, # 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) diff --git a/src/intercom/unstable/calls/types/__init__.py b/src/intercom/unstable/calls/types/__init__.py new file mode 100644 index 00000000..8337976c --- /dev/null +++ b/src/intercom/unstable/calls/types/__init__.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .call import Call + from .list_calls_with_transcripts_response import ListCallsWithTranscriptsResponse + from .list_calls_with_transcripts_response_data_item import ListCallsWithTranscriptsResponseDataItem +_dynamic_imports: typing.Dict[str, str] = { + "Call": ".call", + "ListCallsWithTranscriptsResponse": ".list_calls_with_transcripts_response", + "ListCallsWithTranscriptsResponseDataItem": ".list_calls_with_transcripts_response_data_item", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Call", "ListCallsWithTranscriptsResponse", "ListCallsWithTranscriptsResponseDataItem"] diff --git a/src/intercom/unstable/calls/types/call.py b/src/intercom/unstable/calls/types/call.py new file mode 100644 index 00000000..6f7a3ea3 --- /dev/null +++ b/src/intercom/unstable/calls/types/call.py @@ -0,0 +1,98 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...types.datetime import Datetime + + +class Call(UncheckedBaseModel): + """ + Represents a phone call in Intercom + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `call`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the call. + """ + + conversation_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the conversation associated with the call, if any. + """ + + admin_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the admin associated with the call, if any. + """ + + contact_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the contact associated with the call, if any. + """ + + state: typing.Optional[str] = pydantic.Field(default=None) + """ + The current state of the call. + """ + + initiated_at: typing.Optional[Datetime] = None + answered_at: typing.Optional[Datetime] = None + ended_at: typing.Optional[Datetime] = None + created_at: typing.Optional[Datetime] = None + updated_at: typing.Optional[Datetime] = None + recording_url: typing.Optional[str] = pydantic.Field(default=None) + """ + API URL to download or redirect to the call recording if available. + """ + + transcription_url: typing.Optional[str] = pydantic.Field(default=None) + """ + API URL to the call transcript if available. + """ + + call_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of call. + """ + + direction: typing.Optional[str] = pydantic.Field(default=None) + """ + The direction of the call. + """ + + ended_reason: typing.Optional[str] = pydantic.Field(default=None) + """ + The reason for the call end, if applicable. + """ + + phone: typing.Optional[str] = pydantic.Field(default=None) + """ + The phone number involved in the call, in E.164 format. + """ + + fin_recording_url: typing.Optional[str] = pydantic.Field(default=None) + """ + API URL to the AI Agent (Fin) call recording if available. + """ + + fin_transcription_url: typing.Optional[str] = pydantic.Field(default=None) + """ + API URL to the AI Agent (Fin) call transcript if available. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/calls/types/list_calls_with_transcripts_response.py b/src/intercom/unstable/calls/types/list_calls_with_transcripts_response.py new file mode 100644 index 00000000..840f91b9 --- /dev/null +++ b/src/intercom/unstable/calls/types/list_calls_with_transcripts_response.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .list_calls_with_transcripts_response_data_item import ListCallsWithTranscriptsResponseDataItem + + +class ListCallsWithTranscriptsResponse(UncheckedBaseModel): + type: typing.Optional[str] = None + data: typing.Optional[typing.List[ListCallsWithTranscriptsResponseDataItem]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/calls/types/list_calls_with_transcripts_response_data_item.py b/src/intercom/unstable/calls/types/list_calls_with_transcripts_response_data_item.py new file mode 100644 index 00000000..410bf7fa --- /dev/null +++ b/src/intercom/unstable/calls/types/list_calls_with_transcripts_response_data_item.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from .call import Call + + +class ListCallsWithTranscriptsResponseDataItem(Call): + transcript: typing.Optional[typing.List[typing.Dict[str, typing.Any]]] = pydantic.Field(default=None) + """ + The call transcript if available, otherwise an empty array. + """ + + transcript_status: typing.Optional[str] = pydantic.Field(default=None) + """ + The status of the transcript if available. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/client.py b/src/intercom/unstable/client.py new file mode 100644 index 00000000..dff7cca1 --- /dev/null +++ b/src/intercom/unstable/client.py @@ -0,0 +1,671 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from .raw_client import AsyncRawUnstableClient, RawUnstableClient + +if typing.TYPE_CHECKING: + from .admins.client import AdminsClient, AsyncAdminsClient + from .ai_content.client import AiContentClient, AsyncAiContentClient + from .articles.client import ArticlesClient, AsyncArticlesClient + from .away_status_reasons.client import AsyncAwayStatusReasonsClient, AwayStatusReasonsClient + from .brands.client import AsyncBrandsClient, BrandsClient + from .calls.client import AsyncCallsClient, CallsClient + from .companies.client import AsyncCompaniesClient, CompaniesClient + from .contacts.client import AsyncContactsClient, ContactsClient + from .conversations.client import AsyncConversationsClient, ConversationsClient + from .custom_channel_events.client import AsyncCustomChannelEventsClient, CustomChannelEventsClient + from .custom_object_instances.client import AsyncCustomObjectInstancesClient, CustomObjectInstancesClient + from .data_attributes.client import AsyncDataAttributesClient, DataAttributesClient + from .data_events.client import AsyncDataEventsClient, DataEventsClient + from .data_export.client import AsyncDataExportClient, DataExportClient + from .emails.client import AsyncEmailsClient, EmailsClient + from .export.client import AsyncExportClient, ExportClient + from .help_center.client import AsyncHelpCenterClient, HelpCenterClient + from .internal_articles.client import AsyncInternalArticlesClient, InternalArticlesClient + from .jobs.client import AsyncJobsClient, JobsClient + from .macros.client import AsyncMacrosClient, MacrosClient + from .messages.client import AsyncMessagesClient, MessagesClient + from .news.client import AsyncNewsClient, NewsClient + from .notes.client import AsyncNotesClient, NotesClient + from .segments.client import AsyncSegmentsClient, SegmentsClient + from .subscription_types.client import AsyncSubscriptionTypesClient, SubscriptionTypesClient + from .switch.client import AsyncSwitchClient, SwitchClient + from .tags.client import AsyncTagsClient, TagsClient + from .teams.client import AsyncTeamsClient, TeamsClient + from .ticket_states.client import AsyncTicketStatesClient, TicketStatesClient + from .ticket_type_attributes.client import AsyncTicketTypeAttributesClient, TicketTypeAttributesClient + from .ticket_types.client import AsyncTicketTypesClient, TicketTypesClient + from .tickets.client import AsyncTicketsClient, TicketsClient + from .visitors.client import AsyncVisitorsClient, VisitorsClient + + +class UnstableClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawUnstableClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._admins: typing.Optional[AdminsClient] = None + self._ai_content: typing.Optional[AiContentClient] = None + self._articles: typing.Optional[ArticlesClient] = None + self._away_status_reasons: typing.Optional[AwayStatusReasonsClient] = None + self._export: typing.Optional[ExportClient] = None + self._help_center: typing.Optional[HelpCenterClient] = None + self._internal_articles: typing.Optional[InternalArticlesClient] = None + self._companies: typing.Optional[CompaniesClient] = None + self._notes: typing.Optional[NotesClient] = None + self._contacts: typing.Optional[ContactsClient] = None + self._subscription_types: typing.Optional[SubscriptionTypesClient] = None + self._tags: typing.Optional[TagsClient] = None + self._conversations: typing.Optional[ConversationsClient] = None + self._custom_channel_events: typing.Optional[CustomChannelEventsClient] = None + self._custom_object_instances: typing.Optional[CustomObjectInstancesClient] = None + self._data_attributes: typing.Optional[DataAttributesClient] = None + self._data_events: typing.Optional[DataEventsClient] = None + self._data_export: typing.Optional[DataExportClient] = None + self._jobs: typing.Optional[JobsClient] = None + self._macros: typing.Optional[MacrosClient] = None + self._messages: typing.Optional[MessagesClient] = None + self._news: typing.Optional[NewsClient] = None + self._segments: typing.Optional[SegmentsClient] = None + self._switch: typing.Optional[SwitchClient] = None + self._calls: typing.Optional[CallsClient] = None + self._teams: typing.Optional[TeamsClient] = None + self._ticket_states: typing.Optional[TicketStatesClient] = None + self._ticket_type_attributes: typing.Optional[TicketTypeAttributesClient] = None + self._ticket_types: typing.Optional[TicketTypesClient] = None + self._tickets: typing.Optional[TicketsClient] = None + self._visitors: typing.Optional[VisitorsClient] = None + self._brands: typing.Optional[BrandsClient] = None + self._emails: typing.Optional[EmailsClient] = None + + @property + def with_raw_response(self) -> RawUnstableClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawUnstableClient + """ + return self._raw_client + + @property + def admins(self): + if self._admins is None: + from .admins.client import AdminsClient # noqa: E402 + + self._admins = AdminsClient(client_wrapper=self._client_wrapper) + return self._admins + + @property + def ai_content(self): + if self._ai_content is None: + from .ai_content.client import AiContentClient # noqa: E402 + + self._ai_content = AiContentClient(client_wrapper=self._client_wrapper) + return self._ai_content + + @property + def articles(self): + if self._articles is None: + from .articles.client import ArticlesClient # noqa: E402 + + self._articles = ArticlesClient(client_wrapper=self._client_wrapper) + return self._articles + + @property + def away_status_reasons(self): + if self._away_status_reasons is None: + from .away_status_reasons.client import AwayStatusReasonsClient # noqa: E402 + + self._away_status_reasons = AwayStatusReasonsClient(client_wrapper=self._client_wrapper) + return self._away_status_reasons + + @property + def export(self): + if self._export is None: + from .export.client import ExportClient # noqa: E402 + + self._export = ExportClient(client_wrapper=self._client_wrapper) + return self._export + + @property + def help_center(self): + if self._help_center is None: + from .help_center.client import HelpCenterClient # noqa: E402 + + self._help_center = HelpCenterClient(client_wrapper=self._client_wrapper) + return self._help_center + + @property + def internal_articles(self): + if self._internal_articles is None: + from .internal_articles.client import InternalArticlesClient # noqa: E402 + + self._internal_articles = InternalArticlesClient(client_wrapper=self._client_wrapper) + return self._internal_articles + + @property + def companies(self): + if self._companies is None: + from .companies.client import CompaniesClient # noqa: E402 + + self._companies = CompaniesClient(client_wrapper=self._client_wrapper) + return self._companies + + @property + def notes(self): + if self._notes is None: + from .notes.client import NotesClient # noqa: E402 + + self._notes = NotesClient(client_wrapper=self._client_wrapper) + return self._notes + + @property + def contacts(self): + if self._contacts is None: + from .contacts.client import ContactsClient # noqa: E402 + + self._contacts = ContactsClient(client_wrapper=self._client_wrapper) + return self._contacts + + @property + def subscription_types(self): + if self._subscription_types is None: + from .subscription_types.client import SubscriptionTypesClient # noqa: E402 + + self._subscription_types = SubscriptionTypesClient(client_wrapper=self._client_wrapper) + return self._subscription_types + + @property + def tags(self): + if self._tags is None: + from .tags.client import TagsClient # noqa: E402 + + self._tags = TagsClient(client_wrapper=self._client_wrapper) + return self._tags + + @property + def conversations(self): + if self._conversations is None: + from .conversations.client import ConversationsClient # noqa: E402 + + self._conversations = ConversationsClient(client_wrapper=self._client_wrapper) + return self._conversations + + @property + def custom_channel_events(self): + if self._custom_channel_events is None: + from .custom_channel_events.client import CustomChannelEventsClient # noqa: E402 + + self._custom_channel_events = CustomChannelEventsClient(client_wrapper=self._client_wrapper) + return self._custom_channel_events + + @property + def custom_object_instances(self): + if self._custom_object_instances is None: + from .custom_object_instances.client import CustomObjectInstancesClient # noqa: E402 + + self._custom_object_instances = CustomObjectInstancesClient(client_wrapper=self._client_wrapper) + return self._custom_object_instances + + @property + def data_attributes(self): + if self._data_attributes is None: + from .data_attributes.client import DataAttributesClient # noqa: E402 + + self._data_attributes = DataAttributesClient(client_wrapper=self._client_wrapper) + return self._data_attributes + + @property + def data_events(self): + if self._data_events is None: + from .data_events.client import DataEventsClient # noqa: E402 + + self._data_events = DataEventsClient(client_wrapper=self._client_wrapper) + return self._data_events + + @property + def data_export(self): + if self._data_export is None: + from .data_export.client import DataExportClient # noqa: E402 + + self._data_export = DataExportClient(client_wrapper=self._client_wrapper) + return self._data_export + + @property + def jobs(self): + if self._jobs is None: + from .jobs.client import JobsClient # noqa: E402 + + self._jobs = JobsClient(client_wrapper=self._client_wrapper) + return self._jobs + + @property + def macros(self): + if self._macros is None: + from .macros.client import MacrosClient # noqa: E402 + + self._macros = MacrosClient(client_wrapper=self._client_wrapper) + return self._macros + + @property + def messages(self): + if self._messages is None: + from .messages.client import MessagesClient # noqa: E402 + + self._messages = MessagesClient(client_wrapper=self._client_wrapper) + return self._messages + + @property + def news(self): + if self._news is None: + from .news.client import NewsClient # noqa: E402 + + self._news = NewsClient(client_wrapper=self._client_wrapper) + return self._news + + @property + def segments(self): + if self._segments is None: + from .segments.client import SegmentsClient # noqa: E402 + + self._segments = SegmentsClient(client_wrapper=self._client_wrapper) + return self._segments + + @property + def switch(self): + if self._switch is None: + from .switch.client import SwitchClient # noqa: E402 + + self._switch = SwitchClient(client_wrapper=self._client_wrapper) + return self._switch + + @property + def calls(self): + if self._calls is None: + from .calls.client import CallsClient # noqa: E402 + + self._calls = CallsClient(client_wrapper=self._client_wrapper) + return self._calls + + @property + def teams(self): + if self._teams is None: + from .teams.client import TeamsClient # noqa: E402 + + self._teams = TeamsClient(client_wrapper=self._client_wrapper) + return self._teams + + @property + def ticket_states(self): + if self._ticket_states is None: + from .ticket_states.client import TicketStatesClient # noqa: E402 + + self._ticket_states = TicketStatesClient(client_wrapper=self._client_wrapper) + return self._ticket_states + + @property + def ticket_type_attributes(self): + if self._ticket_type_attributes is None: + from .ticket_type_attributes.client import TicketTypeAttributesClient # noqa: E402 + + self._ticket_type_attributes = TicketTypeAttributesClient(client_wrapper=self._client_wrapper) + return self._ticket_type_attributes + + @property + def ticket_types(self): + if self._ticket_types is None: + from .ticket_types.client import TicketTypesClient # noqa: E402 + + self._ticket_types = TicketTypesClient(client_wrapper=self._client_wrapper) + return self._ticket_types + + @property + def tickets(self): + if self._tickets is None: + from .tickets.client import TicketsClient # noqa: E402 + + self._tickets = TicketsClient(client_wrapper=self._client_wrapper) + return self._tickets + + @property + def visitors(self): + if self._visitors is None: + from .visitors.client import VisitorsClient # noqa: E402 + + self._visitors = VisitorsClient(client_wrapper=self._client_wrapper) + return self._visitors + + @property + def brands(self): + if self._brands is None: + from .brands.client import BrandsClient # noqa: E402 + + self._brands = BrandsClient(client_wrapper=self._client_wrapper) + return self._brands + + @property + def emails(self): + if self._emails is None: + from .emails.client import EmailsClient # noqa: E402 + + self._emails = EmailsClient(client_wrapper=self._client_wrapper) + return self._emails + + +class AsyncUnstableClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawUnstableClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._admins: typing.Optional[AsyncAdminsClient] = None + self._ai_content: typing.Optional[AsyncAiContentClient] = None + self._articles: typing.Optional[AsyncArticlesClient] = None + self._away_status_reasons: typing.Optional[AsyncAwayStatusReasonsClient] = None + self._export: typing.Optional[AsyncExportClient] = None + self._help_center: typing.Optional[AsyncHelpCenterClient] = None + self._internal_articles: typing.Optional[AsyncInternalArticlesClient] = None + self._companies: typing.Optional[AsyncCompaniesClient] = None + self._notes: typing.Optional[AsyncNotesClient] = None + self._contacts: typing.Optional[AsyncContactsClient] = None + self._subscription_types: typing.Optional[AsyncSubscriptionTypesClient] = None + self._tags: typing.Optional[AsyncTagsClient] = None + self._conversations: typing.Optional[AsyncConversationsClient] = None + self._custom_channel_events: typing.Optional[AsyncCustomChannelEventsClient] = None + self._custom_object_instances: typing.Optional[AsyncCustomObjectInstancesClient] = None + self._data_attributes: typing.Optional[AsyncDataAttributesClient] = None + self._data_events: typing.Optional[AsyncDataEventsClient] = None + self._data_export: typing.Optional[AsyncDataExportClient] = None + self._jobs: typing.Optional[AsyncJobsClient] = None + self._macros: typing.Optional[AsyncMacrosClient] = None + self._messages: typing.Optional[AsyncMessagesClient] = None + self._news: typing.Optional[AsyncNewsClient] = None + self._segments: typing.Optional[AsyncSegmentsClient] = None + self._switch: typing.Optional[AsyncSwitchClient] = None + self._calls: typing.Optional[AsyncCallsClient] = None + self._teams: typing.Optional[AsyncTeamsClient] = None + self._ticket_states: typing.Optional[AsyncTicketStatesClient] = None + self._ticket_type_attributes: typing.Optional[AsyncTicketTypeAttributesClient] = None + self._ticket_types: typing.Optional[AsyncTicketTypesClient] = None + self._tickets: typing.Optional[AsyncTicketsClient] = None + self._visitors: typing.Optional[AsyncVisitorsClient] = None + self._brands: typing.Optional[AsyncBrandsClient] = None + self._emails: typing.Optional[AsyncEmailsClient] = None + + @property + def with_raw_response(self) -> AsyncRawUnstableClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawUnstableClient + """ + return self._raw_client + + @property + def admins(self): + if self._admins is None: + from .admins.client import AsyncAdminsClient # noqa: E402 + + self._admins = AsyncAdminsClient(client_wrapper=self._client_wrapper) + return self._admins + + @property + def ai_content(self): + if self._ai_content is None: + from .ai_content.client import AsyncAiContentClient # noqa: E402 + + self._ai_content = AsyncAiContentClient(client_wrapper=self._client_wrapper) + return self._ai_content + + @property + def articles(self): + if self._articles is None: + from .articles.client import AsyncArticlesClient # noqa: E402 + + self._articles = AsyncArticlesClient(client_wrapper=self._client_wrapper) + return self._articles + + @property + def away_status_reasons(self): + if self._away_status_reasons is None: + from .away_status_reasons.client import AsyncAwayStatusReasonsClient # noqa: E402 + + self._away_status_reasons = AsyncAwayStatusReasonsClient(client_wrapper=self._client_wrapper) + return self._away_status_reasons + + @property + def export(self): + if self._export is None: + from .export.client import AsyncExportClient # noqa: E402 + + self._export = AsyncExportClient(client_wrapper=self._client_wrapper) + return self._export + + @property + def help_center(self): + if self._help_center is None: + from .help_center.client import AsyncHelpCenterClient # noqa: E402 + + self._help_center = AsyncHelpCenterClient(client_wrapper=self._client_wrapper) + return self._help_center + + @property + def internal_articles(self): + if self._internal_articles is None: + from .internal_articles.client import AsyncInternalArticlesClient # noqa: E402 + + self._internal_articles = AsyncInternalArticlesClient(client_wrapper=self._client_wrapper) + return self._internal_articles + + @property + def companies(self): + if self._companies is None: + from .companies.client import AsyncCompaniesClient # noqa: E402 + + self._companies = AsyncCompaniesClient(client_wrapper=self._client_wrapper) + return self._companies + + @property + def notes(self): + if self._notes is None: + from .notes.client import AsyncNotesClient # noqa: E402 + + self._notes = AsyncNotesClient(client_wrapper=self._client_wrapper) + return self._notes + + @property + def contacts(self): + if self._contacts is None: + from .contacts.client import AsyncContactsClient # noqa: E402 + + self._contacts = AsyncContactsClient(client_wrapper=self._client_wrapper) + return self._contacts + + @property + def subscription_types(self): + if self._subscription_types is None: + from .subscription_types.client import AsyncSubscriptionTypesClient # noqa: E402 + + self._subscription_types = AsyncSubscriptionTypesClient(client_wrapper=self._client_wrapper) + return self._subscription_types + + @property + def tags(self): + if self._tags is None: + from .tags.client import AsyncTagsClient # noqa: E402 + + self._tags = AsyncTagsClient(client_wrapper=self._client_wrapper) + return self._tags + + @property + def conversations(self): + if self._conversations is None: + from .conversations.client import AsyncConversationsClient # noqa: E402 + + self._conversations = AsyncConversationsClient(client_wrapper=self._client_wrapper) + return self._conversations + + @property + def custom_channel_events(self): + if self._custom_channel_events is None: + from .custom_channel_events.client import AsyncCustomChannelEventsClient # noqa: E402 + + self._custom_channel_events = AsyncCustomChannelEventsClient(client_wrapper=self._client_wrapper) + return self._custom_channel_events + + @property + def custom_object_instances(self): + if self._custom_object_instances is None: + from .custom_object_instances.client import AsyncCustomObjectInstancesClient # noqa: E402 + + self._custom_object_instances = AsyncCustomObjectInstancesClient(client_wrapper=self._client_wrapper) + return self._custom_object_instances + + @property + def data_attributes(self): + if self._data_attributes is None: + from .data_attributes.client import AsyncDataAttributesClient # noqa: E402 + + self._data_attributes = AsyncDataAttributesClient(client_wrapper=self._client_wrapper) + return self._data_attributes + + @property + def data_events(self): + if self._data_events is None: + from .data_events.client import AsyncDataEventsClient # noqa: E402 + + self._data_events = AsyncDataEventsClient(client_wrapper=self._client_wrapper) + return self._data_events + + @property + def data_export(self): + if self._data_export is None: + from .data_export.client import AsyncDataExportClient # noqa: E402 + + self._data_export = AsyncDataExportClient(client_wrapper=self._client_wrapper) + return self._data_export + + @property + def jobs(self): + if self._jobs is None: + from .jobs.client import AsyncJobsClient # noqa: E402 + + self._jobs = AsyncJobsClient(client_wrapper=self._client_wrapper) + return self._jobs + + @property + def macros(self): + if self._macros is None: + from .macros.client import AsyncMacrosClient # noqa: E402 + + self._macros = AsyncMacrosClient(client_wrapper=self._client_wrapper) + return self._macros + + @property + def messages(self): + if self._messages is None: + from .messages.client import AsyncMessagesClient # noqa: E402 + + self._messages = AsyncMessagesClient(client_wrapper=self._client_wrapper) + return self._messages + + @property + def news(self): + if self._news is None: + from .news.client import AsyncNewsClient # noqa: E402 + + self._news = AsyncNewsClient(client_wrapper=self._client_wrapper) + return self._news + + @property + def segments(self): + if self._segments is None: + from .segments.client import AsyncSegmentsClient # noqa: E402 + + self._segments = AsyncSegmentsClient(client_wrapper=self._client_wrapper) + return self._segments + + @property + def switch(self): + if self._switch is None: + from .switch.client import AsyncSwitchClient # noqa: E402 + + self._switch = AsyncSwitchClient(client_wrapper=self._client_wrapper) + return self._switch + + @property + def calls(self): + if self._calls is None: + from .calls.client import AsyncCallsClient # noqa: E402 + + self._calls = AsyncCallsClient(client_wrapper=self._client_wrapper) + return self._calls + + @property + def teams(self): + if self._teams is None: + from .teams.client import AsyncTeamsClient # noqa: E402 + + self._teams = AsyncTeamsClient(client_wrapper=self._client_wrapper) + return self._teams + + @property + def ticket_states(self): + if self._ticket_states is None: + from .ticket_states.client import AsyncTicketStatesClient # noqa: E402 + + self._ticket_states = AsyncTicketStatesClient(client_wrapper=self._client_wrapper) + return self._ticket_states + + @property + def ticket_type_attributes(self): + if self._ticket_type_attributes is None: + from .ticket_type_attributes.client import AsyncTicketTypeAttributesClient # noqa: E402 + + self._ticket_type_attributes = AsyncTicketTypeAttributesClient(client_wrapper=self._client_wrapper) + return self._ticket_type_attributes + + @property + def ticket_types(self): + if self._ticket_types is None: + from .ticket_types.client import AsyncTicketTypesClient # noqa: E402 + + self._ticket_types = AsyncTicketTypesClient(client_wrapper=self._client_wrapper) + return self._ticket_types + + @property + def tickets(self): + if self._tickets is None: + from .tickets.client import AsyncTicketsClient # noqa: E402 + + self._tickets = AsyncTicketsClient(client_wrapper=self._client_wrapper) + return self._tickets + + @property + def visitors(self): + if self._visitors is None: + from .visitors.client import AsyncVisitorsClient # noqa: E402 + + self._visitors = AsyncVisitorsClient(client_wrapper=self._client_wrapper) + return self._visitors + + @property + def brands(self): + if self._brands is None: + from .brands.client import AsyncBrandsClient # noqa: E402 + + self._brands = AsyncBrandsClient(client_wrapper=self._client_wrapper) + return self._brands + + @property + def emails(self): + if self._emails is None: + from .emails.client import AsyncEmailsClient # noqa: E402 + + self._emails = AsyncEmailsClient(client_wrapper=self._client_wrapper) + return self._emails diff --git a/src/intercom/unstable/companies/__init__.py b/src/intercom/unstable/companies/__init__.py new file mode 100644 index 00000000..dc869795 --- /dev/null +++ b/src/intercom/unstable/companies/__init__.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import Company, CompanyNotes, CompanyPlan, CompanySegments, CompanyTags +_dynamic_imports: typing.Dict[str, str] = { + "Company": ".types", + "CompanyNotes": ".types", + "CompanyPlan": ".types", + "CompanySegments": ".types", + "CompanyTags": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Company", "CompanyNotes", "CompanyPlan", "CompanySegments", "CompanyTags"] diff --git a/src/intercom/unstable/companies/client.py b/src/intercom/unstable/companies/client.py new file mode 100644 index 00000000..2564cd30 --- /dev/null +++ b/src/intercom/unstable/companies/client.py @@ -0,0 +1,1070 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..types.company_attached_contacts import CompanyAttachedContacts +from ..types.company_attached_segments import CompanyAttachedSegments +from ..types.company_list import CompanyList +from ..types.company_scroll import CompanyScroll +from ..types.deleted_company_object import DeletedCompanyObject +from .raw_client import AsyncRawCompaniesClient, RawCompaniesClient +from .types.company import Company + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class CompaniesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawCompaniesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawCompaniesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawCompaniesClient + """ + return self._raw_client + + def retrieve_company( + self, + *, + name: typing.Optional[str] = None, + company_id: typing.Optional[str] = None, + tag_id: typing.Optional[str] = None, + segment_id: typing.Optional[str] = None, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> CompanyList: + """ + You can fetch a single company by passing in `company_id` or `name`. + + `https://api.intercom.io/companies?name={name}` + + `https://api.intercom.io/companies?company_id={company_id}` + + You can fetch all companies and filter by `segment_id` or `tag_id` as a query parameter. + + `https://api.intercom.io/companies?tag_id={tag_id}` + + `https://api.intercom.io/companies?segment_id={segment_id}` + + Parameters + ---------- + name : typing.Optional[str] + The `name` of the company to filter by. + + company_id : typing.Optional[str] + The `company_id` of the company to filter by. + + tag_id : typing.Optional[str] + The `tag_id` of the company to filter by. + + segment_id : typing.Optional[str] + The `segment_id` of the company to filter by. + + 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 + ------- + CompanyList + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.companies.retrieve_company( + name="my company", + company_id="12345", + tag_id="678910", + segment_id="98765", + page=1, + per_page=1, + ) + """ + _response = self._raw_client.retrieve_company( + name=name, + company_id=company_id, + tag_id=tag_id, + segment_id=segment_id, + page=page, + per_page=per_page, + request_options=request_options, + ) + return _response.data + + def create_or_update_company( + self, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> Company: + """ + You can create or update a company. + + Companies will be only visible in Intercom when there is at least one associated user. + + Companies are looked up via `company_id` in a `POST` request, if not found via `company_id`, the new company will be created, if found, that company will be updated. + + {% admonition type="warning" name="Using `company_id`" %} + You can set a unique `company_id` value when creating a company. However, it is not possible to update `company_id`. Be sure to set a unique value once upon creation of the company. + {% /admonition %} + + Parameters + ---------- + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Company + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.companies.create_or_update_company( + request={"key": "value"}, + ) + """ + _response = self._raw_client.create_or_update_company(request=request, request_options=request_options) + return _response.data + + def retrieve_a_company_by_id(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Company: + """ + You can fetch a single company. + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Company + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.companies.retrieve_a_company_by_id( + id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + ) + """ + _response = self._raw_client.retrieve_a_company_by_id(id, request_options=request_options) + return _response.data + + def update_company(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Company: + """ + You can update a single company using the Intercom provisioned `id`. + + {% admonition type="warning" name="Using `company_id`" %} + When updating a company it is not possible to update `company_id`. This can only be set once upon creation of the company. + {% /admonition %} + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Company + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.companies.update_company( + id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + ) + """ + _response = self._raw_client.update_company(id, request_options=request_options) + return _response.data + + def delete_company( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeletedCompanyObject: + """ + You can delete a single company. + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeletedCompanyObject + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.companies.delete_company( + id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + ) + """ + _response = self._raw_client.delete_company(id, request_options=request_options) + return _response.data + + def list_attached_contacts( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> CompanyAttachedContacts: + """ + You can fetch a list of all contacts that belong to a company. + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CompanyAttachedContacts + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.companies.list_attached_contacts( + id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + ) + """ + _response = self._raw_client.list_attached_contacts(id, request_options=request_options) + return _response.data + + def list_attached_segments_for_companies( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> CompanyAttachedSegments: + """ + You can fetch a list of all segments that belong to a company. + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CompanyAttachedSegments + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.companies.list_attached_segments_for_companies( + id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + ) + """ + _response = self._raw_client.list_attached_segments_for_companies(id, request_options=request_options) + return _response.data + + def list_all_companies( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + order: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> CompanyList: + """ + You can list companies. The company list is sorted by the `last_request_at` field and by default is ordered descending, most recently requested first. + + Note that the API does not include companies who have no associated users in list responses. + + When using the Companies endpoint and the pages object to iterate through the returned companies, there is a limit of 10,000 Companies that can be returned. If you need to list or iterate on more than 10,000 Companies, please use the [Scroll API](https://developers.intercom.com/reference#iterating-over-all-companies). + {% admonition type="warning" name="Pagination" %} + You can use pagination to limit the number of results returned. The default is `20` 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 return per page. Defaults to 15 + + order : typing.Optional[str] + `asc` or `desc`. Return the companies in ascending or descending order. Defaults to desc + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CompanyList + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.companies.list_all_companies( + page=1, + per_page=1, + order="desc", + ) + """ + _response = self._raw_client.list_all_companies( + page=page, per_page=per_page, order=order, request_options=request_options + ) + return _response.data + + def scroll_over_all_companies( + self, *, scroll_param: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[CompanyScroll]: + """ + The `list all companies` functionality does not work well for huge datasets, and can result in errors and performance problems when paging deeply. The Scroll API provides an efficient mechanism for iterating over all companies in a dataset. + + - Each app can only have 1 scroll open at a time. You'll get an error message if you try to have more than one open per app. + - If the scroll isn't used for 1 minute, it expires and calls with that scroll param will fail + - If the end of the scroll is reached, "companies" will be empty and the scroll parameter will expire + + {% admonition type="info" name="Scroll Parameter" %} + You can get the first page of companies by simply sending a GET request to the scroll endpoint. + For subsequent requests you will need to use the scroll parameter from the response. + {% /admonition %} + {% admonition type="danger" name="Scroll network timeouts" %} + Since scroll is often used on large datasets network errors such as timeouts can be encountered. When this occurs you will see a HTTP 500 error with the following message: + "Request failed due to an internal network error. Please restart the scroll operation." + If this happens, you will need to restart your scroll query: It is not possible to continue from a specific point when using scroll. + {% /admonition %} + + Parameters + ---------- + scroll_param : typing.Optional[str] + + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[CompanyScroll] + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.companies.scroll_over_all_companies( + scroll_param="scroll_param", + ) + """ + _response = self._raw_client.scroll_over_all_companies( + scroll_param=scroll_param, request_options=request_options + ) + return _response.data + + def attach_contact_to_a_company( + self, id: str, *, company_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> Company: + """ + You can attach a company to a single contact. + + Parameters + ---------- + id : str + The unique identifier for the contact which is given by Intercom + + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Company + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.companies.attach_contact_to_a_company( + id="id", + company_id="6762f09a1bb69f9f2193bb34", + ) + """ + _response = self._raw_client.attach_contact_to_a_company( + id, company_id=company_id, request_options=request_options + ) + return _response.data + + def detach_contact_from_a_company( + self, contact_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Company: + """ + You can detach a company from a single contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Company + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.companies.detach_contact_from_a_company( + contact_id="58a430d35458202d41b1e65b", + id="58a430d35458202d41b1e65b", + ) + """ + _response = self._raw_client.detach_contact_from_a_company(contact_id, id, request_options=request_options) + return _response.data + + +class AsyncCompaniesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawCompaniesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawCompaniesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawCompaniesClient + """ + return self._raw_client + + async def retrieve_company( + self, + *, + name: typing.Optional[str] = None, + company_id: typing.Optional[str] = None, + tag_id: typing.Optional[str] = None, + segment_id: typing.Optional[str] = None, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> CompanyList: + """ + You can fetch a single company by passing in `company_id` or `name`. + + `https://api.intercom.io/companies?name={name}` + + `https://api.intercom.io/companies?company_id={company_id}` + + You can fetch all companies and filter by `segment_id` or `tag_id` as a query parameter. + + `https://api.intercom.io/companies?tag_id={tag_id}` + + `https://api.intercom.io/companies?segment_id={segment_id}` + + Parameters + ---------- + name : typing.Optional[str] + The `name` of the company to filter by. + + company_id : typing.Optional[str] + The `company_id` of the company to filter by. + + tag_id : typing.Optional[str] + The `tag_id` of the company to filter by. + + segment_id : typing.Optional[str] + The `segment_id` of the company to filter by. + + 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 + ------- + CompanyList + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.companies.retrieve_company( + name="my company", + company_id="12345", + tag_id="678910", + segment_id="98765", + page=1, + per_page=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.retrieve_company( + name=name, + company_id=company_id, + tag_id=tag_id, + segment_id=segment_id, + page=page, + per_page=per_page, + request_options=request_options, + ) + return _response.data + + async def create_or_update_company( + self, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> Company: + """ + You can create or update a company. + + Companies will be only visible in Intercom when there is at least one associated user. + + Companies are looked up via `company_id` in a `POST` request, if not found via `company_id`, the new company will be created, if found, that company will be updated. + + {% admonition type="warning" name="Using `company_id`" %} + You can set a unique `company_id` value when creating a company. However, it is not possible to update `company_id`. Be sure to set a unique value once upon creation of the company. + {% /admonition %} + + Parameters + ---------- + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Company + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.companies.create_or_update_company( + request={"key": "value"}, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_or_update_company(request=request, request_options=request_options) + return _response.data + + async def retrieve_a_company_by_id( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Company: + """ + You can fetch a single company. + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Company + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.companies.retrieve_a_company_by_id( + id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.retrieve_a_company_by_id(id, request_options=request_options) + return _response.data + + async def update_company(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Company: + """ + You can update a single company using the Intercom provisioned `id`. + + {% admonition type="warning" name="Using `company_id`" %} + When updating a company it is not possible to update `company_id`. This can only be set once upon creation of the company. + {% /admonition %} + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Company + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.companies.update_company( + id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update_company(id, request_options=request_options) + return _response.data + + async def delete_company( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeletedCompanyObject: + """ + You can delete a single company. + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeletedCompanyObject + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.companies.delete_company( + id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_company(id, request_options=request_options) + return _response.data + + async def list_attached_contacts( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> CompanyAttachedContacts: + """ + You can fetch a list of all contacts that belong to a company. + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CompanyAttachedContacts + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.companies.list_attached_contacts( + id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_attached_contacts(id, request_options=request_options) + return _response.data + + async def list_attached_segments_for_companies( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> CompanyAttachedSegments: + """ + You can fetch a list of all segments that belong to a company. + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CompanyAttachedSegments + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.companies.list_attached_segments_for_companies( + id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_attached_segments_for_companies(id, request_options=request_options) + return _response.data + + async def list_all_companies( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + order: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> CompanyList: + """ + You can list companies. The company list is sorted by the `last_request_at` field and by default is ordered descending, most recently requested first. + + Note that the API does not include companies who have no associated users in list responses. + + When using the Companies endpoint and the pages object to iterate through the returned companies, there is a limit of 10,000 Companies that can be returned. If you need to list or iterate on more than 10,000 Companies, please use the [Scroll API](https://developers.intercom.com/reference#iterating-over-all-companies). + {% admonition type="warning" name="Pagination" %} + You can use pagination to limit the number of results returned. The default is `20` 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 return per page. Defaults to 15 + + order : typing.Optional[str] + `asc` or `desc`. Return the companies in ascending or descending order. Defaults to desc + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CompanyList + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.companies.list_all_companies( + page=1, + per_page=1, + order="desc", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_all_companies( + page=page, per_page=per_page, order=order, request_options=request_options + ) + return _response.data + + async def scroll_over_all_companies( + self, *, scroll_param: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[CompanyScroll]: + """ + The `list all companies` functionality does not work well for huge datasets, and can result in errors and performance problems when paging deeply. The Scroll API provides an efficient mechanism for iterating over all companies in a dataset. + + - Each app can only have 1 scroll open at a time. You'll get an error message if you try to have more than one open per app. + - If the scroll isn't used for 1 minute, it expires and calls with that scroll param will fail + - If the end of the scroll is reached, "companies" will be empty and the scroll parameter will expire + + {% admonition type="info" name="Scroll Parameter" %} + You can get the first page of companies by simply sending a GET request to the scroll endpoint. + For subsequent requests you will need to use the scroll parameter from the response. + {% /admonition %} + {% admonition type="danger" name="Scroll network timeouts" %} + Since scroll is often used on large datasets network errors such as timeouts can be encountered. When this occurs you will see a HTTP 500 error with the following message: + "Request failed due to an internal network error. Please restart the scroll operation." + If this happens, you will need to restart your scroll query: It is not possible to continue from a specific point when using scroll. + {% /admonition %} + + Parameters + ---------- + scroll_param : typing.Optional[str] + + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[CompanyScroll] + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.companies.scroll_over_all_companies( + scroll_param="scroll_param", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.scroll_over_all_companies( + scroll_param=scroll_param, request_options=request_options + ) + return _response.data + + async def attach_contact_to_a_company( + self, id: str, *, company_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> Company: + """ + You can attach a company to a single contact. + + Parameters + ---------- + id : str + The unique identifier for the contact which is given by Intercom + + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Company + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.companies.attach_contact_to_a_company( + id="id", + company_id="6762f09a1bb69f9f2193bb34", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.attach_contact_to_a_company( + id, company_id=company_id, request_options=request_options + ) + return _response.data + + async def detach_contact_from_a_company( + self, contact_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Company: + """ + You can detach a company from a single contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Company + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.companies.detach_contact_from_a_company( + contact_id="58a430d35458202d41b1e65b", + id="58a430d35458202d41b1e65b", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.detach_contact_from_a_company( + contact_id, id, request_options=request_options + ) + return _response.data diff --git a/src/intercom/unstable/companies/raw_client.py b/src/intercom/unstable/companies/raw_client.py new file mode 100644 index 00000000..ca705dd5 --- /dev/null +++ b/src/intercom/unstable/companies/raw_client.py @@ -0,0 +1,1580 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.bad_request_error import BadRequestError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.company_attached_contacts import CompanyAttachedContacts +from ..types.company_attached_segments import CompanyAttachedSegments +from ..types.company_list import CompanyList +from ..types.company_scroll import CompanyScroll +from ..types.deleted_company_object import DeletedCompanyObject +from ..types.error import Error +from .types.company import Company + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawCompaniesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def retrieve_company( + self, + *, + name: typing.Optional[str] = None, + company_id: typing.Optional[str] = None, + tag_id: typing.Optional[str] = None, + segment_id: typing.Optional[str] = None, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[CompanyList]: + """ + You can fetch a single company by passing in `company_id` or `name`. + + `https://api.intercom.io/companies?name={name}` + + `https://api.intercom.io/companies?company_id={company_id}` + + You can fetch all companies and filter by `segment_id` or `tag_id` as a query parameter. + + `https://api.intercom.io/companies?tag_id={tag_id}` + + `https://api.intercom.io/companies?segment_id={segment_id}` + + Parameters + ---------- + name : typing.Optional[str] + The `name` of the company to filter by. + + company_id : typing.Optional[str] + The `company_id` of the company to filter by. + + tag_id : typing.Optional[str] + The `tag_id` of the company to filter by. + + segment_id : typing.Optional[str] + The `segment_id` of the company to filter by. + + 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 + ------- + HttpResponse[CompanyList] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + "companies", + method="GET", + params={ + "name": name, + "company_id": company_id, + "tag_id": tag_id, + "segment_id": segment_id, + "page": page, + "per_page": per_page, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CompanyList, + construct_type( + type_=CompanyList, # 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 create_or_update_company( + self, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Company]: + """ + You can create or update a company. + + Companies will be only visible in Intercom when there is at least one associated user. + + Companies are looked up via `company_id` in a `POST` request, if not found via `company_id`, the new company will be created, if found, that company will be updated. + + {% admonition type="warning" name="Using `company_id`" %} + You can set a unique `company_id` value when creating a company. However, it is not possible to update `company_id`. Be sure to set a unique value once upon creation of the company. + {% /admonition %} + + Parameters + ---------- + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Company] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + "companies", + method="POST", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Company, + construct_type( + type_=Company, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 retrieve_a_company_by_id( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Company]: + """ + You can fetch a single company. + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Company] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Company, + construct_type( + type_=Company, # 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 update_company( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Company]: + """ + You can update a single company using the Intercom provisioned `id`. + + {% admonition type="warning" name="Using `company_id`" %} + When updating a company it is not possible to update `company_id`. This can only be set once upon creation of the company. + {% /admonition %} + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Company] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(id)}", + method="PUT", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Company, + construct_type( + type_=Company, # 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 delete_company( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DeletedCompanyObject]: + """ + You can delete a single company. + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeletedCompanyObject] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeletedCompanyObject, + construct_type( + type_=DeletedCompanyObject, # 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_contacts( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[CompanyAttachedContacts]: + """ + You can fetch a list of all contacts that belong to a company. + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CompanyAttachedContacts] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(id)}/contacts", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CompanyAttachedContacts, + construct_type( + type_=CompanyAttachedContacts, # 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_segments_for_companies( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[CompanyAttachedSegments]: + """ + You can fetch a list of all segments that belong to a company. + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CompanyAttachedSegments] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(id)}/segments", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CompanyAttachedSegments, + construct_type( + type_=CompanyAttachedSegments, # 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_all_companies( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + order: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[CompanyList]: + """ + You can list companies. The company list is sorted by the `last_request_at` field and by default is ordered descending, most recently requested first. + + Note that the API does not include companies who have no associated users in list responses. + + When using the Companies endpoint and the pages object to iterate through the returned companies, there is a limit of 10,000 Companies that can be returned. If you need to list or iterate on more than 10,000 Companies, please use the [Scroll API](https://developers.intercom.com/reference#iterating-over-all-companies). + {% admonition type="warning" name="Pagination" %} + You can use pagination to limit the number of results returned. The default is `20` 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 return per page. Defaults to 15 + + order : typing.Optional[str] + `asc` or `desc`. Return the companies in ascending or descending order. Defaults to desc + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CompanyList] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + "companies/list", + method="POST", + params={ + "page": page, + "per_page": per_page, + "order": order, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CompanyList, + construct_type( + type_=CompanyList, # 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 scroll_over_all_companies( + self, *, scroll_param: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[typing.Optional[CompanyScroll]]: + """ + The `list all companies` functionality does not work well for huge datasets, and can result in errors and performance problems when paging deeply. The Scroll API provides an efficient mechanism for iterating over all companies in a dataset. + + - Each app can only have 1 scroll open at a time. You'll get an error message if you try to have more than one open per app. + - If the scroll isn't used for 1 minute, it expires and calls with that scroll param will fail + - If the end of the scroll is reached, "companies" will be empty and the scroll parameter will expire + + {% admonition type="info" name="Scroll Parameter" %} + You can get the first page of companies by simply sending a GET request to the scroll endpoint. + For subsequent requests you will need to use the scroll parameter from the response. + {% /admonition %} + {% admonition type="danger" name="Scroll network timeouts" %} + Since scroll is often used on large datasets network errors such as timeouts can be encountered. When this occurs you will see a HTTP 500 error with the following message: + "Request failed due to an internal network error. Please restart the scroll operation." + If this happens, you will need to restart your scroll query: It is not possible to continue from a specific point when using scroll. + {% /admonition %} + + Parameters + ---------- + scroll_param : typing.Optional[str] + + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[CompanyScroll]] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + "companies/scroll", + method="GET", + params={ + "scroll_param": scroll_param, + }, + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[CompanyScroll], + construct_type( + type_=typing.Optional[CompanyScroll], # 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 attach_contact_to_a_company( + self, id: str, *, company_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Company]: + """ + You can attach a company to a single contact. + + Parameters + ---------- + id : str + The unique identifier for the contact which is given by Intercom + + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Company] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(id)}/companies", + method="POST", + json={ + "company_id": company_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Company, + construct_type( + type_=Company, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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_contact_from_a_company( + self, contact_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Company]: + """ + You can detach a company from a single contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Company] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(contact_id)}/companies/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Company, + construct_type( + type_=Company, # 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) + + +class AsyncRawCompaniesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def retrieve_company( + self, + *, + name: typing.Optional[str] = None, + company_id: typing.Optional[str] = None, + tag_id: typing.Optional[str] = None, + segment_id: typing.Optional[str] = None, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[CompanyList]: + """ + You can fetch a single company by passing in `company_id` or `name`. + + `https://api.intercom.io/companies?name={name}` + + `https://api.intercom.io/companies?company_id={company_id}` + + You can fetch all companies and filter by `segment_id` or `tag_id` as a query parameter. + + `https://api.intercom.io/companies?tag_id={tag_id}` + + `https://api.intercom.io/companies?segment_id={segment_id}` + + Parameters + ---------- + name : typing.Optional[str] + The `name` of the company to filter by. + + company_id : typing.Optional[str] + The `company_id` of the company to filter by. + + tag_id : typing.Optional[str] + The `tag_id` of the company to filter by. + + segment_id : typing.Optional[str] + The `segment_id` of the company to filter by. + + 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 + ------- + AsyncHttpResponse[CompanyList] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + "companies", + method="GET", + params={ + "name": name, + "company_id": company_id, + "tag_id": tag_id, + "segment_id": segment_id, + "page": page, + "per_page": per_page, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CompanyList, + construct_type( + type_=CompanyList, # 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 create_or_update_company( + self, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Company]: + """ + You can create or update a company. + + Companies will be only visible in Intercom when there is at least one associated user. + + Companies are looked up via `company_id` in a `POST` request, if not found via `company_id`, the new company will be created, if found, that company will be updated. + + {% admonition type="warning" name="Using `company_id`" %} + You can set a unique `company_id` value when creating a company. However, it is not possible to update `company_id`. Be sure to set a unique value once upon creation of the company. + {% /admonition %} + + Parameters + ---------- + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Company] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + "companies", + method="POST", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Company, + construct_type( + type_=Company, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 retrieve_a_company_by_id( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Company]: + """ + You can fetch a single company. + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Company] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Company, + construct_type( + type_=Company, # 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 update_company( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Company]: + """ + You can update a single company using the Intercom provisioned `id`. + + {% admonition type="warning" name="Using `company_id`" %} + When updating a company it is not possible to update `company_id`. This can only be set once upon creation of the company. + {% /admonition %} + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Company] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(id)}", + method="PUT", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Company, + construct_type( + type_=Company, # 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 delete_company( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DeletedCompanyObject]: + """ + You can delete a single company. + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeletedCompanyObject] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeletedCompanyObject, + construct_type( + type_=DeletedCompanyObject, # 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_contacts( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[CompanyAttachedContacts]: + """ + You can fetch a list of all contacts that belong to a company. + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CompanyAttachedContacts] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(id)}/contacts", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CompanyAttachedContacts, + construct_type( + type_=CompanyAttachedContacts, # 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_segments_for_companies( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[CompanyAttachedSegments]: + """ + You can fetch a list of all segments that belong to a company. + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CompanyAttachedSegments] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(id)}/segments", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CompanyAttachedSegments, + construct_type( + type_=CompanyAttachedSegments, # 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_all_companies( + self, + *, + page: typing.Optional[int] = None, + per_page: typing.Optional[int] = None, + order: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[CompanyList]: + """ + You can list companies. The company list is sorted by the `last_request_at` field and by default is ordered descending, most recently requested first. + + Note that the API does not include companies who have no associated users in list responses. + + When using the Companies endpoint and the pages object to iterate through the returned companies, there is a limit of 10,000 Companies that can be returned. If you need to list or iterate on more than 10,000 Companies, please use the [Scroll API](https://developers.intercom.com/reference#iterating-over-all-companies). + {% admonition type="warning" name="Pagination" %} + You can use pagination to limit the number of results returned. The default is `20` 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 return per page. Defaults to 15 + + order : typing.Optional[str] + `asc` or `desc`. Return the companies in ascending or descending order. Defaults to desc + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CompanyList] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + "companies/list", + method="POST", + params={ + "page": page, + "per_page": per_page, + "order": order, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CompanyList, + construct_type( + type_=CompanyList, # 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 scroll_over_all_companies( + self, *, scroll_param: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[typing.Optional[CompanyScroll]]: + """ + The `list all companies` functionality does not work well for huge datasets, and can result in errors and performance problems when paging deeply. The Scroll API provides an efficient mechanism for iterating over all companies in a dataset. + + - Each app can only have 1 scroll open at a time. You'll get an error message if you try to have more than one open per app. + - If the scroll isn't used for 1 minute, it expires and calls with that scroll param will fail + - If the end of the scroll is reached, "companies" will be empty and the scroll parameter will expire + + {% admonition type="info" name="Scroll Parameter" %} + You can get the first page of companies by simply sending a GET request to the scroll endpoint. + For subsequent requests you will need to use the scroll parameter from the response. + {% /admonition %} + {% admonition type="danger" name="Scroll network timeouts" %} + Since scroll is often used on large datasets network errors such as timeouts can be encountered. When this occurs you will see a HTTP 500 error with the following message: + "Request failed due to an internal network error. Please restart the scroll operation." + If this happens, you will need to restart your scroll query: It is not possible to continue from a specific point when using scroll. + {% /admonition %} + + Parameters + ---------- + scroll_param : typing.Optional[str] + + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[CompanyScroll]] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + "companies/scroll", + method="GET", + params={ + "scroll_param": scroll_param, + }, + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[CompanyScroll], + construct_type( + type_=typing.Optional[CompanyScroll], # 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 attach_contact_to_a_company( + self, id: str, *, company_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Company]: + """ + You can attach a company to a single contact. + + Parameters + ---------- + id : str + The unique identifier for the contact which is given by Intercom + + company_id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Company] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(id)}/companies", + method="POST", + json={ + "company_id": company_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Company, + construct_type( + type_=Company, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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_contact_from_a_company( + self, contact_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Company]: + """ + You can detach a company from a single contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Company] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(contact_id)}/companies/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Company, + construct_type( + type_=Company, # 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) diff --git a/src/intercom/unstable/companies/types/__init__.py b/src/intercom/unstable/companies/types/__init__.py new file mode 100644 index 00000000..ec0dcd23 --- /dev/null +++ b/src/intercom/unstable/companies/types/__init__.py @@ -0,0 +1,44 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .company import Company + from .company_notes import CompanyNotes + from .company_plan import CompanyPlan + from .company_segments import CompanySegments + from .company_tags import CompanyTags +_dynamic_imports: typing.Dict[str, str] = { + "Company": ".company", + "CompanyNotes": ".company_notes", + "CompanyPlan": ".company_plan", + "CompanySegments": ".company_segments", + "CompanyTags": ".company_tags", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Company", "CompanyNotes", "CompanyPlan", "CompanySegments", "CompanyTags"] diff --git a/src/intercom/unstable/companies/types/company.py b/src/intercom/unstable/companies/types/company.py new file mode 100644 index 00000000..f09b2184 --- /dev/null +++ b/src/intercom/unstable/companies/types/company.py @@ -0,0 +1,122 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .company_notes import CompanyNotes +from .company_plan import CompanyPlan +from .company_segments import CompanySegments +from .company_tags import CompanyTags + + +class Company(UncheckedBaseModel): + """ + Companies allow you to represent organizations using your product. Each company will have its own description and be associated with contacts. You can fetch, create, update and list companies. + """ + + type: typing.Optional[typing.Literal["company"]] = pydantic.Field(default=None) + """ + Value is `company` + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom defined id representing the company. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the company. + """ + + app_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom defined code of the workspace the company is associated to. + """ + + plan: typing.Optional[CompanyPlan] = None + company_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The company id you have defined for the company. + """ + + remote_created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the company was created by you. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the company was added in Intercom. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The last time the company was updated. + """ + + last_request_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the company last recorded making a request. + """ + + size: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of employees in the company. + """ + + website: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL for the company website. + """ + + industry: typing.Optional[str] = pydantic.Field(default=None) + """ + The industry that the company operates in. + """ + + monthly_spend: typing.Optional[int] = pydantic.Field(default=None) + """ + How much revenue the company generates for your business. + """ + + session_count: typing.Optional[int] = pydantic.Field(default=None) + """ + How many sessions the company has recorded. + """ + + user_count: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of users in the company. + """ + + custom_attributes: typing.Optional[typing.Dict[str, str]] = pydantic.Field(default=None) + """ + The custom attributes you have set on the company. + """ + + tags: typing.Optional[CompanyTags] = pydantic.Field(default=None) + """ + The list of tags associated with the company + """ + + segments: typing.Optional[CompanySegments] = pydantic.Field(default=None) + """ + The list of segments associated with the company + """ + + notes: typing.Optional[CompanyNotes] = pydantic.Field(default=None) + """ + The list of notes associated with the company + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/companies/types/company_notes.py b/src/intercom/unstable/companies/types/company_notes.py new file mode 100644 index 00000000..4ec70d89 --- /dev/null +++ b/src/intercom/unstable/companies/types/company_notes.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...notes.types.company_note import CompanyNote + + +class CompanyNotes(UncheckedBaseModel): + """ + The list of notes associated with the company + """ + + type: typing.Optional[typing.Literal["note.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + notes: typing.Optional[typing.List[CompanyNote]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/companies/types/company_plan.py b/src/intercom/unstable/companies/types/company_plan.py new file mode 100644 index 00000000..d75c5a43 --- /dev/null +++ b/src/intercom/unstable/companies/types/company_plan.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class CompanyPlan(UncheckedBaseModel): + type: typing.Optional[str] = pydantic.Field(default=None) + """ + Value is always "plan" + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the plan + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the plan + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/companies/types/company_segments.py b/src/intercom/unstable/companies/types/company_segments.py new file mode 100644 index 00000000..7377aba5 --- /dev/null +++ b/src/intercom/unstable/companies/types/company_segments.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...segments.types.segment import Segment + + +class CompanySegments(UncheckedBaseModel): + """ + The list of segments associated with the company + """ + + type: typing.Optional[typing.Literal["segment.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + segments: typing.Optional[typing.List[Segment]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/companies/types/company_tags.py b/src/intercom/unstable/companies/types/company_tags.py new file mode 100644 index 00000000..77779e3f --- /dev/null +++ b/src/intercom/unstable/companies/types/company_tags.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class CompanyTags(UncheckedBaseModel): + """ + The list of tags associated with the company + """ + + type: typing.Optional[typing.Literal["tag.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + tags: typing.Optional[typing.List[typing.Any]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/contacts/__init__.py b/src/intercom/unstable/contacts/__init__.py new file mode 100644 index 00000000..f2302263 --- /dev/null +++ b/src/intercom/unstable/contacts/__init__.py @@ -0,0 +1,58 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + Contact, + ContactAvatar, + CreateContactResponse, + MergeContactResponse, + ShowContactByExternalIdResponse, + ShowContactResponse, + UpdateContactResponse, + ) +_dynamic_imports: typing.Dict[str, str] = { + "Contact": ".types", + "ContactAvatar": ".types", + "CreateContactResponse": ".types", + "MergeContactResponse": ".types", + "ShowContactByExternalIdResponse": ".types", + "ShowContactResponse": ".types", + "UpdateContactResponse": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "Contact", + "ContactAvatar", + "CreateContactResponse", + "MergeContactResponse", + "ShowContactByExternalIdResponse", + "ShowContactResponse", + "UpdateContactResponse", +] diff --git a/src/intercom/unstable/contacts/client.py b/src/intercom/unstable/contacts/client.py new file mode 100644 index 00000000..9f42c669 --- /dev/null +++ b/src/intercom/unstable/contacts/client.py @@ -0,0 +1,1565 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ...types.create_contact_request_two import CreateContactRequestTwo +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.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 .raw_client import AsyncRawContactsClient, RawContactsClient +from .types.create_contact_response import CreateContactResponse +from .types.merge_contact_response import MergeContactResponse +from .types.show_contact_by_external_id_response import ShowContactByExternalIdResponse +from .types.show_contact_response import ShowContactResponse +from .types.update_contact_response import UpdateContactResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class ContactsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawContactsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawContactsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawContactsClient + """ + return self._raw_client + + def list_companies_for_a_contact( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContactAttachedCompanies: + """ + You can fetch a list of companies that are associated to a contact. + + Parameters + ---------- + id : str + The unique identifier for the contact which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContactAttachedCompanies + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.contacts.list_companies_for_a_contact( + id="63a07ddf05a32042dffac965", + ) + """ + _response = self._raw_client.list_companies_for_a_contact(id, request_options=request_options) + return _response.data + + def list_segments_for_a_contact( + self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + ------- + ContactSegments + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.contacts.list_segments_for_a_contact( + contact_id="63a07ddf05a32042dffac965", + ) + """ + _response = self._raw_client.list_segments_for_a_contact(contact_id, request_options=request_options) + return _response.data + + def list_subscriptions_for_a_contact( + self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + ------- + SubscriptionTypeList + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.contacts.list_subscriptions_for_a_contact( + contact_id="63a07ddf05a32042dffac965", + ) + """ + _response = self._raw_client.list_subscriptions_for_a_contact(contact_id, request_options=request_options) + return _response.data + + def list_tags_for_a_contact( + self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + ------- + TagList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.contacts.list_tags_for_a_contact( + contact_id="63a07ddf05a32042dffac965", + ) + """ + _response = self._raw_client.list_tags_for_a_contact(contact_id, request_options=request_options) + return _response.data + + def show_contact(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> ShowContactResponse: + """ + You can fetch the details of a single contact. + + Parameters + ---------- + id : str + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ShowContactResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.contacts.show_contact( + id="63a07ddf05a32042dffac965", + ) + """ + _response = self._raw_client.show_contact(id, request_options=request_options) + return _response.data + + def update_contact( + self, + 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, + ) -> UpdateContactResponse: + """ + 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 + ---------- + 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 + ------- + UpdateContactResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.contacts.update_contact( + id="63a07ddf05a32042dffac965", + email="joebloggs@intercom.io", + name="joe bloggs", + ) + """ + _response = self._raw_client.update_contact( + id, + 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, + request_options=request_options, + ) + return _response.data + + def delete_contact(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> ContactDeleted: + """ + You can delete a single contact. + + Parameters + ---------- + id : str + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContactDeleted + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.contacts.delete_contact( + id="id", + ) + """ + _response = self._raw_client.delete_contact(id, request_options=request_options) + return _response.data + + def merge_contact( + self, + *, + from_: typing.Optional[str] = OMIT, + into: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> MergeContactResponse: + """ + You can merge a contact with a `role` of `lead` into a contact with a `role` of `user`. + + Parameters + ---------- + from_ : typing.Optional[str] + The unique identifier for the contact to merge away from. Must be a lead. + + into : 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 + ------- + MergeContactResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.contacts.merge_contact( + from_="6762f0d51bb69f9f2193bb7f", + into="6762f0d51bb69f9f2193bb80", + ) + """ + _response = self._raw_client.merge_contact(from_=from_, into=into, request_options=request_options) + return _response.data + + def search_contacts( + self, + *, + query: SearchRequestQuery, + pagination: typing.Optional[StartingAfterPaging] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> 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
Accepts user or lead | + | name | String | + | avatar | String | + | owner_id | Integer | + | email | String | + | email_domain | String | + | phone | String | + | formatted_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
Shortcut for `OR` queries
Values must be in Array | + | NIN | All | Not In
Shortcut for `OR !` queries
Values must be in Array | + | > | Integer
Date (UNIX Timestamp) | Greater than | + | < | Integer
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 + ------- + ContactList + successful + + Examples + -------- + from intercom import Intercom + from intercom.unstable import ( + MultipleFilterSearchRequest, + SingleFilterSearchRequest, + StartingAfterPaging, + ) + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.contacts.search_contacts( + query=MultipleFilterSearchRequest( + operator="AND", + value=[ + SingleFilterSearchRequest( + field="created_at", + operator=">", + value="1306054154", + ) + ], + ), + pagination=StartingAfterPaging( + per_page=5, + ), + ) + """ + _response = self._raw_client.search_contacts( + query=query, pagination=pagination, request_options=request_options + ) + return _response.data + + def list_contacts(self, *, request_options: typing.Optional[RequestOptions] = None) -> 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 + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContactList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.contacts.list_contacts() + """ + _response = self._raw_client.list_contacts(request_options=request_options) + return _response.data + + def create_contact( + self, *, request: CreateContactRequestTwo, request_options: typing.Optional[RequestOptions] = None + ) -> CreateContactResponse: + """ + You can create a new contact (ie. user or lead). + + Parameters + ---------- + request : CreateContactRequestTwo + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CreateContactResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.contacts.create_contact( + request={"email": "joebloggs@intercom.io"}, + ) + """ + _response = self._raw_client.create_contact(request=request, request_options=request_options) + return _response.data + + def show_contact_by_external_id( + self, external_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + ------- + ShowContactByExternalIdResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.contacts.show_contact_by_external_id( + external_id="cdd29344-5e0c-4ef0-ac56-f9ba2979bc27", + ) + """ + _response = self._raw_client.show_contact_by_external_id(external_id, request_options=request_options) + return _response.data + + def archive_contact(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> ContactArchived: + """ + You can archive a single contact. + + Parameters + ---------- + id : str + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContactArchived + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.contacts.archive_contact( + id="63a07ddf05a32042dffac965", + ) + """ + _response = self._raw_client.archive_contact(id, request_options=request_options) + return _response.data + + def unarchive_contact( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContactUnarchived: + """ + You can unarchive a single contact. + + Parameters + ---------- + id : str + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContactUnarchived + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.contacts.unarchive_contact( + id="63a07ddf05a32042dffac965", + ) + """ + _response = self._raw_client.unarchive_contact(id, request_options=request_options) + return _response.data + + def block_contact(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> ContactBlocked: + """ + Block a single contact.
**Note:** conversations of the contact will also be archived during the process.
More details in [FAQ How do I block Inbox spam?](https://www.intercom.com/help/en/articles/8838656-inbox-faqs) + + Parameters + ---------- + id : str + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContactBlocked + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.contacts.block_contact( + id="63a07ddf05a32042dffac965", + ) + """ + _response = self._raw_client.block_contact(id, request_options=request_options) + return _response.data + + +class AsyncContactsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawContactsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawContactsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawContactsClient + """ + return self._raw_client + + async def list_companies_for_a_contact( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContactAttachedCompanies: + """ + You can fetch a list of companies that are associated to a contact. + + Parameters + ---------- + id : str + The unique identifier for the contact which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContactAttachedCompanies + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.contacts.list_companies_for_a_contact( + id="63a07ddf05a32042dffac965", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_companies_for_a_contact(id, request_options=request_options) + return _response.data + + async def list_segments_for_a_contact( + self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + ------- + ContactSegments + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.contacts.list_segments_for_a_contact( + contact_id="63a07ddf05a32042dffac965", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_segments_for_a_contact(contact_id, request_options=request_options) + return _response.data + + async def list_subscriptions_for_a_contact( + self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + ------- + SubscriptionTypeList + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.contacts.list_subscriptions_for_a_contact( + contact_id="63a07ddf05a32042dffac965", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_subscriptions_for_a_contact(contact_id, request_options=request_options) + return _response.data + + async def list_tags_for_a_contact( + self, contact_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + ------- + TagList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.contacts.list_tags_for_a_contact( + contact_id="63a07ddf05a32042dffac965", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_tags_for_a_contact(contact_id, request_options=request_options) + return _response.data + + async def show_contact( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ShowContactResponse: + """ + You can fetch the details of a single contact. + + Parameters + ---------- + id : str + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ShowContactResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.contacts.show_contact( + id="63a07ddf05a32042dffac965", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.show_contact(id, request_options=request_options) + return _response.data + + async def update_contact( + self, + 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, + ) -> UpdateContactResponse: + """ + 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 + ---------- + 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 + ------- + UpdateContactResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.contacts.update_contact( + id="63a07ddf05a32042dffac965", + email="joebloggs@intercom.io", + name="joe bloggs", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update_contact( + id, + 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, + request_options=request_options, + ) + return _response.data + + async def delete_contact( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContactDeleted: + """ + You can delete a single contact. + + Parameters + ---------- + id : str + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContactDeleted + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.contacts.delete_contact( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_contact(id, request_options=request_options) + return _response.data + + async def merge_contact( + self, + *, + from_: typing.Optional[str] = OMIT, + into: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> MergeContactResponse: + """ + You can merge a contact with a `role` of `lead` into a contact with a `role` of `user`. + + Parameters + ---------- + from_ : typing.Optional[str] + The unique identifier for the contact to merge away from. Must be a lead. + + into : 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 + ------- + MergeContactResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.contacts.merge_contact( + from_="6762f0d51bb69f9f2193bb7f", + into="6762f0d51bb69f9f2193bb80", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.merge_contact(from_=from_, into=into, request_options=request_options) + return _response.data + + async def search_contacts( + self, + *, + query: SearchRequestQuery, + pagination: typing.Optional[StartingAfterPaging] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> 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
Accepts user or lead | + | name | String | + | avatar | String | + | owner_id | Integer | + | email | String | + | email_domain | String | + | phone | String | + | formatted_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
Shortcut for `OR` queries
Values must be in Array | + | NIN | All | Not In
Shortcut for `OR !` queries
Values must be in Array | + | > | Integer
Date (UNIX Timestamp) | Greater than | + | < | Integer
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 + ------- + ContactList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.unstable import ( + MultipleFilterSearchRequest, + SingleFilterSearchRequest, + StartingAfterPaging, + ) + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.contacts.search_contacts( + query=MultipleFilterSearchRequest( + operator="AND", + value=[ + SingleFilterSearchRequest( + field="created_at", + operator=">", + value="1306054154", + ) + ], + ), + pagination=StartingAfterPaging( + per_page=5, + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.search_contacts( + query=query, pagination=pagination, request_options=request_options + ) + return _response.data + + async def list_contacts(self, *, request_options: typing.Optional[RequestOptions] = None) -> 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 + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContactList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.contacts.list_contacts() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_contacts(request_options=request_options) + return _response.data + + async def create_contact( + self, *, request: CreateContactRequestTwo, request_options: typing.Optional[RequestOptions] = None + ) -> CreateContactResponse: + """ + You can create a new contact (ie. user or lead). + + Parameters + ---------- + request : CreateContactRequestTwo + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CreateContactResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.contacts.create_contact( + request={"email": "joebloggs@intercom.io"}, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_contact(request=request, request_options=request_options) + return _response.data + + async def show_contact_by_external_id( + self, external_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + ------- + ShowContactByExternalIdResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.contacts.show_contact_by_external_id( + external_id="cdd29344-5e0c-4ef0-ac56-f9ba2979bc27", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.show_contact_by_external_id(external_id, request_options=request_options) + return _response.data + + async def archive_contact( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContactArchived: + """ + You can archive a single contact. + + Parameters + ---------- + id : str + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContactArchived + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.contacts.archive_contact( + id="63a07ddf05a32042dffac965", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.archive_contact(id, request_options=request_options) + return _response.data + + async def unarchive_contact( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContactUnarchived: + """ + You can unarchive a single contact. + + Parameters + ---------- + id : str + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContactUnarchived + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.contacts.unarchive_contact( + id="63a07ddf05a32042dffac965", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.unarchive_contact(id, request_options=request_options) + return _response.data + + async def block_contact( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> ContactBlocked: + """ + Block a single contact.
**Note:** conversations of the contact will also be archived during the process.
More details in [FAQ How do I block Inbox spam?](https://www.intercom.com/help/en/articles/8838656-inbox-faqs) + + Parameters + ---------- + id : str + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ContactBlocked + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.contacts.block_contact( + id="63a07ddf05a32042dffac965", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.block_contact(id, request_options=request_options) + return _response.data diff --git a/src/intercom/unstable/contacts/raw_client.py b/src/intercom/unstable/contacts/raw_client.py new file mode 100644 index 00000000..b6c527c5 --- /dev/null +++ b/src/intercom/unstable/contacts/raw_client.py @@ -0,0 +1,1989 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.serialization import convert_and_respect_annotation_metadata +from ...core.unchecked_base_model import construct_type +from ...types.create_contact_request_two import CreateContactRequestTwo +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +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.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.create_contact_response import CreateContactResponse +from .types.merge_contact_response import MergeContactResponse +from .types.show_contact_by_external_id_response import ShowContactByExternalIdResponse +from .types.show_contact_response import ShowContactResponse +from .types.update_contact_response import UpdateContactResponse + +# 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_companies_for_a_contact( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ContactAttachedCompanies]: + """ + You can fetch a list of companies that are associated to a contact. + + Parameters + ---------- + id : str + The unique identifier for the contact which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ContactAttachedCompanies] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(id)}/companies", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ContactAttachedCompanies, + construct_type( + type_=ContactAttachedCompanies, # 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_segments_for_a_contact( + 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_subscriptions_for_a_contact( + 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 list_tags_for_a_contact( + 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 show_contact( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ShowContactResponse]: + """ + You can fetch the details of a single contact. + + Parameters + ---------- + id : str + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ShowContactResponse] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ShowContactResponse, + construct_type( + type_=ShowContactResponse, # 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_contact( + self, + 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[UpdateContactResponse]: + """ + 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 + ---------- + 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[UpdateContactResponse] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(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( + UpdateContactResponse, + construct_type( + type_=UpdateContactResponse, # 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_contact( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ContactDeleted]: + """ + You can delete a single contact. + + Parameters + ---------- + id : str + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ContactDeleted] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(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_contact( + self, + *, + from_: typing.Optional[str] = OMIT, + into: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[MergeContactResponse]: + """ + You can merge a contact with a `role` of `lead` into a contact with a `role` of `user`. + + Parameters + ---------- + from_ : typing.Optional[str] + The unique identifier for the contact to merge away from. Must be a lead. + + into : 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[MergeContactResponse] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "contacts/merge", + method="POST", + json={ + "from": from_, + "into": into, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + MergeContactResponse, + construct_type( + type_=MergeContactResponse, # 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_contacts( + self, + *, + query: SearchRequestQuery, + pagination: typing.Optional[StartingAfterPaging] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[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
Accepts user or lead | + | name | String | + | avatar | String | + | owner_id | Integer | + | email | String | + | email_domain | String | + | phone | String | + | formatted_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
Shortcut for `OR` queries
Values must be in Array | + | NIN | All | Not In
Shortcut for `OR !` queries
Values must be in Array | + | > | Integer
Date (UNIX Timestamp) | Greater than | + | < | Integer
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 + ------- + HttpResponse[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: + _data = typing.cast( + ContactList, + construct_type( + type_=ContactList, # 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 list_contacts(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[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 + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ContactList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "contacts", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ContactList, + construct_type( + type_=ContactList, # 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 create_contact( + self, *, request: CreateContactRequestTwo, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[CreateContactResponse]: + """ + You can create a new contact (ie. user or lead). + + Parameters + ---------- + request : CreateContactRequestTwo + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CreateContactResponse] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "contacts", + method="POST", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CreateContactResponse, + construct_type( + type_=CreateContactResponse, # 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_contact( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ContactArchived]: + """ + You can archive a single contact. + + Parameters + ---------- + id : str + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ContactArchived] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(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_contact( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ContactUnarchived]: + """ + You can unarchive a single contact. + + Parameters + ---------- + id : str + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ContactUnarchived] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(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, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ContactBlocked]: + """ + Block a single contact.
**Note:** conversations of the contact will also be archived during the process.
More details in [FAQ How do I block Inbox spam?](https://www.intercom.com/help/en/articles/8838656-inbox-faqs) + + Parameters + ---------- + id : str + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ContactBlocked] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(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_companies_for_a_contact( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ContactAttachedCompanies]: + """ + You can fetch a list of companies that are associated to a contact. + + Parameters + ---------- + id : str + The unique identifier for the contact which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ContactAttachedCompanies] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(id)}/companies", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ContactAttachedCompanies, + construct_type( + type_=ContactAttachedCompanies, # 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_segments_for_a_contact( + 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_subscriptions_for_a_contact( + 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 list_tags_for_a_contact( + 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 show_contact( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ShowContactResponse]: + """ + You can fetch the details of a single contact. + + Parameters + ---------- + id : str + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ShowContactResponse] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ShowContactResponse, + construct_type( + type_=ShowContactResponse, # 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_contact( + self, + 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[UpdateContactResponse]: + """ + 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 + ---------- + 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[UpdateContactResponse] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(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( + UpdateContactResponse, + construct_type( + type_=UpdateContactResponse, # 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_contact( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ContactDeleted]: + """ + You can delete a single contact. + + Parameters + ---------- + id : str + 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(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_contact( + self, + *, + from_: typing.Optional[str] = OMIT, + into: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[MergeContactResponse]: + """ + You can merge a contact with a `role` of `lead` into a contact with a `role` of `user`. + + Parameters + ---------- + from_ : typing.Optional[str] + The unique identifier for the contact to merge away from. Must be a lead. + + into : 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[MergeContactResponse] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "contacts/merge", + method="POST", + json={ + "from": from_, + "into": into, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + MergeContactResponse, + construct_type( + type_=MergeContactResponse, # 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_contacts( + self, + *, + query: SearchRequestQuery, + pagination: typing.Optional[StartingAfterPaging] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[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
Accepts user or lead | + | name | String | + | avatar | String | + | owner_id | Integer | + | email | String | + | email_domain | String | + | phone | String | + | formatted_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
Shortcut for `OR` queries
Values must be in Array | + | NIN | All | Not In
Shortcut for `OR !` queries
Values must be in Array | + | > | Integer
Date (UNIX Timestamp) | Greater than | + | < | Integer
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 + ------- + AsyncHttpResponse[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: + _data = typing.cast( + ContactList, + construct_type( + type_=ContactList, # 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 list_contacts( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[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 + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ContactList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "contacts", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ContactList, + construct_type( + type_=ContactList, # 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 create_contact( + self, *, request: CreateContactRequestTwo, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[CreateContactResponse]: + """ + You can create a new contact (ie. user or lead). + + Parameters + ---------- + request : CreateContactRequestTwo + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CreateContactResponse] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "contacts", + method="POST", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CreateContactResponse, + construct_type( + type_=CreateContactResponse, # 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_contact( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ContactArchived]: + """ + You can archive a single contact. + + Parameters + ---------- + id : str + 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(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_contact( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ContactUnarchived]: + """ + You can unarchive a single contact. + + Parameters + ---------- + id : str + 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(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, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ContactBlocked]: + """ + Block a single contact.
**Note:** conversations of the contact will also be archived during the process.
More details in [FAQ How do I block Inbox spam?](https://www.intercom.com/help/en/articles/8838656-inbox-faqs) + + Parameters + ---------- + id : str + 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(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) diff --git a/src/intercom/unstable/contacts/types/__init__.py b/src/intercom/unstable/contacts/types/__init__.py new file mode 100644 index 00000000..18985f51 --- /dev/null +++ b/src/intercom/unstable/contacts/types/__init__.py @@ -0,0 +1,56 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .contact import Contact + from .contact_avatar import ContactAvatar + from .create_contact_response import CreateContactResponse + from .merge_contact_response import MergeContactResponse + from .show_contact_by_external_id_response import ShowContactByExternalIdResponse + from .show_contact_response import ShowContactResponse + from .update_contact_response import UpdateContactResponse +_dynamic_imports: typing.Dict[str, str] = { + "Contact": ".contact", + "ContactAvatar": ".contact_avatar", + "CreateContactResponse": ".create_contact_response", + "MergeContactResponse": ".merge_contact_response", + "ShowContactByExternalIdResponse": ".show_contact_by_external_id_response", + "ShowContactResponse": ".show_contact_response", + "UpdateContactResponse": ".update_contact_response", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "Contact", + "ContactAvatar", + "CreateContactResponse", + "MergeContactResponse", + "ShowContactByExternalIdResponse", + "ShowContactResponse", + "UpdateContactResponse", +] diff --git a/src/intercom/unstable/contacts/types/contact.py b/src/intercom/unstable/contacts/types/contact.py new file mode 100644 index 00000000..3025f494 --- /dev/null +++ b/src/intercom/unstable/contacts/types/contact.py @@ -0,0 +1,235 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...types.contact_companies import ContactCompanies +from ...types.contact_location import ContactLocation +from ...types.contact_notes import ContactNotes +from ...types.contact_social_profiles import ContactSocialProfiles +from ...types.contact_tags import ContactTags +from .contact_avatar import ContactAvatar + + +class Contact(UncheckedBaseModel): + """ + Contacts represent your leads and users in Intercom. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of object. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the contact which is given by Intercom. + """ + + external_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the contact which is provided by the Client. + """ + + workspace_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the workspace which the contact belongs to. + """ + + role: typing.Optional[str] = pydantic.Field(default=None) + """ + The role of the contact. + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + The contact's email. + """ + + email_domain: typing.Optional[str] = pydantic.Field(default=None) + """ + The contact's email domain. + """ + + phone: typing.Optional[str] = pydantic.Field(default=None) + """ + The contacts phone. + """ + + formatted_phone: typing.Optional[str] = pydantic.Field(default=None) + """ + The contacts phone number normalized to the E164 format + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The contacts name. + """ + + owner_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of an admin that has been assigned account ownership of the contact. + """ + + has_hard_bounced: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the contact has had an email sent to them hard bounce. + """ + + marked_email_as_spam: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the contact has marked an email sent to them as spam. + """ + + unsubscribed_from_emails: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the contact is unsubscribed from emails. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + (UNIX timestamp) The time when the contact was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + (UNIX timestamp) The time when the contact was last updated. + """ + + signed_up_at: typing.Optional[int] = pydantic.Field(default=None) + """ + (UNIX timestamp) The time specified for when a contact signed up. + """ + + last_seen_at: typing.Optional[int] = pydantic.Field(default=None) + """ + (UNIX timestamp) The time when the contact was last seen (either where the Intercom Messenger was installed or when specified manually). + """ + + last_replied_at: typing.Optional[int] = pydantic.Field(default=None) + """ + (UNIX timestamp) The time when the contact last messaged in. + """ + + last_contacted_at: typing.Optional[int] = pydantic.Field(default=None) + """ + (UNIX timestamp) The time when the contact was last messaged. + """ + + last_email_opened_at: typing.Optional[int] = pydantic.Field(default=None) + """ + (UNIX timestamp) The time when the contact last opened an email. + """ + + last_email_clicked_at: typing.Optional[int] = pydantic.Field(default=None) + """ + (UNIX timestamp) The time when the contact last clicked a link in an email. + """ + + language_override: typing.Optional[str] = pydantic.Field(default=None) + """ + A preferred language setting for the contact, used by the Intercom Messenger even if their browser settings change. + """ + + browser: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the browser which the contact is using. + """ + + browser_version: typing.Optional[str] = pydantic.Field(default=None) + """ + The version of the browser which the contact is using. + """ + + browser_language: typing.Optional[str] = pydantic.Field(default=None) + """ + The language set by the browser which the contact is using. + """ + + os: typing.Optional[str] = pydantic.Field(default=None) + """ + The operating system which the contact is using. + """ + + android_app_name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the Android app which the contact is using. + """ + + android_app_version: typing.Optional[str] = pydantic.Field(default=None) + """ + The version of the Android app which the contact is using. + """ + + android_device: typing.Optional[str] = pydantic.Field(default=None) + """ + The Android device which the contact is using. + """ + + android_os_version: typing.Optional[str] = pydantic.Field(default=None) + """ + The version of the Android OS which the contact is using. + """ + + android_sdk_version: typing.Optional[str] = pydantic.Field(default=None) + """ + The version of the Android SDK which the contact is using. + """ + + android_last_seen_at: typing.Optional[int] = pydantic.Field(default=None) + """ + (UNIX timestamp) The time when the contact was last seen on an Android device. + """ + + ios_app_name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the iOS app which the contact is using. + """ + + ios_app_version: typing.Optional[str] = pydantic.Field(default=None) + """ + The version of the iOS app which the contact is using. + """ + + ios_device: typing.Optional[str] = pydantic.Field(default=None) + """ + The iOS device which the contact is using. + """ + + ios_os_version: typing.Optional[str] = pydantic.Field(default=None) + """ + The version of iOS which the contact is using. + """ + + ios_sdk_version: typing.Optional[str] = pydantic.Field(default=None) + """ + The version of the iOS SDK which the contact is using. + """ + + ios_last_seen_at: typing.Optional[int] = pydantic.Field(default=None) + """ + (UNIX timestamp) The last time the contact used the iOS app. + """ + + custom_attributes: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + The custom attributes which are set for the contact. + """ + + avatar: typing.Optional[ContactAvatar] = None + tags: typing.Optional[ContactTags] = None + notes: typing.Optional[ContactNotes] = None + companies: typing.Optional[ContactCompanies] = None + location: typing.Optional[ContactLocation] = None + social_profiles: typing.Optional[ContactSocialProfiles] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/contacts/types/contact_avatar.py b/src/intercom/unstable/contacts/types/contact_avatar.py new file mode 100644 index 00000000..c390330f --- /dev/null +++ b/src/intercom/unstable/contacts/types/contact_avatar.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class ContactAvatar(UncheckedBaseModel): + type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of object + """ + + image_url: typing.Optional[str] = pydantic.Field(default=None) + """ + An image URL containing the avatar of a contact. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/contacts/types/create_contact_response.py b/src/intercom/unstable/contacts/types/create_contact_response.py new file mode 100644 index 00000000..9aaf58c5 --- /dev/null +++ b/src/intercom/unstable/contacts/types/create_contact_response.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact import Contact + + +class CreateContactResponse(Contact): + enabled_push_messaging: typing.Optional[bool] = pydantic.Field(default=None) + """ + If the user has enabled push messaging. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/contacts/types/merge_contact_response.py b/src/intercom/unstable/contacts/types/merge_contact_response.py new file mode 100644 index 00000000..092b0f6d --- /dev/null +++ b/src/intercom/unstable/contacts/types/merge_contact_response.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact import Contact + + +class MergeContactResponse(Contact): + enabled_push_messaging: typing.Optional[bool] = pydantic.Field(default=None) + """ + If the user has enabled push messaging. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/contacts/types/show_contact_by_external_id_response.py b/src/intercom/unstable/contacts/types/show_contact_by_external_id_response.py new file mode 100644 index 00000000..eea9b869 --- /dev/null +++ b/src/intercom/unstable/contacts/types/show_contact_by_external_id_response.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact import Contact + + +class ShowContactByExternalIdResponse(Contact): + enabled_push_messaging: typing.Optional[bool] = pydantic.Field(default=None) + """ + If the user has enabled push messaging. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/contacts/types/show_contact_response.py b/src/intercom/unstable/contacts/types/show_contact_response.py new file mode 100644 index 00000000..110862cf --- /dev/null +++ b/src/intercom/unstable/contacts/types/show_contact_response.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact import Contact + + +class ShowContactResponse(Contact): + enabled_push_messaging: typing.Optional[bool] = pydantic.Field(default=None) + """ + If the user has enabled push messaging. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/contacts/types/update_contact_response.py b/src/intercom/unstable/contacts/types/update_contact_response.py new file mode 100644 index 00000000..069a235f --- /dev/null +++ b/src/intercom/unstable/contacts/types/update_contact_response.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact import Contact + + +class UpdateContactResponse(Contact): + enabled_push_messaging: typing.Optional[bool] = pydantic.Field(default=None) + """ + If the user has enabled push messaging. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/conversations/__init__.py b/src/intercom/unstable/conversations/__init__.py new file mode 100644 index 00000000..91beb0e5 --- /dev/null +++ b/src/intercom/unstable/conversations/__init__.py @@ -0,0 +1,79 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + AttachContactToConversationRequestCustomer, + AttachContactToConversationRequestCustomerCustomer, + AttachContactToConversationRequestCustomerIntercomUserId, + AttachContactToConversationRequestCustomerUserId, + Conversation, + ConversationPriority, + ConversationState, + CreateConversationRequestFrom, + CreateConversationRequestFromType, + ManageConversationRequestBody, + ManageConversationRequestBody_Assignment, + ManageConversationRequestBody_Close, + ManageConversationRequestBody_Open, + ManageConversationRequestBody_Snoozed, + ) +_dynamic_imports: typing.Dict[str, str] = { + "AttachContactToConversationRequestCustomer": ".types", + "AttachContactToConversationRequestCustomerCustomer": ".types", + "AttachContactToConversationRequestCustomerIntercomUserId": ".types", + "AttachContactToConversationRequestCustomerUserId": ".types", + "Conversation": ".types", + "ConversationPriority": ".types", + "ConversationState": ".types", + "CreateConversationRequestFrom": ".types", + "CreateConversationRequestFromType": ".types", + "ManageConversationRequestBody": ".types", + "ManageConversationRequestBody_Assignment": ".types", + "ManageConversationRequestBody_Close": ".types", + "ManageConversationRequestBody_Open": ".types", + "ManageConversationRequestBody_Snoozed": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "AttachContactToConversationRequestCustomer", + "AttachContactToConversationRequestCustomerCustomer", + "AttachContactToConversationRequestCustomerIntercomUserId", + "AttachContactToConversationRequestCustomerUserId", + "Conversation", + "ConversationPriority", + "ConversationState", + "CreateConversationRequestFrom", + "CreateConversationRequestFromType", + "ManageConversationRequestBody", + "ManageConversationRequestBody_Assignment", + "ManageConversationRequestBody_Close", + "ManageConversationRequestBody_Open", + "ManageConversationRequestBody_Snoozed", +] diff --git a/src/intercom/unstable/conversations/client.py b/src/intercom/unstable/conversations/client.py new file mode 100644 index 00000000..00289a6b --- /dev/null +++ b/src/intercom/unstable/conversations/client.py @@ -0,0 +1,1574 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..messages.types.message import Message +from ..tickets.types.ticket import Ticket +from ..types.conversation_deleted import ConversationDeleted +from ..types.conversation_list import ConversationList +from ..types.custom_attributes import CustomAttributes +from ..types.redact_conversation_request import RedactConversationRequest +from ..types.reply_conversation_request_body import ReplyConversationRequestBody +from ..types.search_request_query import SearchRequestQuery +from ..types.starting_after_paging import StartingAfterPaging +from ..types.ticket_request_custom_attributes import TicketRequestCustomAttributes +from .raw_client import AsyncRawConversationsClient, RawConversationsClient +from .types.attach_contact_to_conversation_request_customer import AttachContactToConversationRequestCustomer +from .types.conversation import Conversation +from .types.create_conversation_request_from import CreateConversationRequestFrom +from .types.manage_conversation_request_body import ManageConversationRequestBody + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class ConversationsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawConversationsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawConversationsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawConversationsClient + """ + return self._raw_client + + def list_conversations( + self, + *, + per_page: typing.Optional[int] = None, + starting_after: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ConversationList: + """ + You can fetch a list of all conversations. + + You can optionally request the result page size and the cursor to start after to fetch the result. + {% admonition type="warning" name="Pagination" %} + You can use pagination to limit the number of results returned. The default is `20` 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 + ---------- + per_page : typing.Optional[int] + How many results per page + + starting_after : typing.Optional[str] + String used to get the next page of conversations. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ConversationList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.conversations.list_conversations( + per_page=1, + starting_after="starting_after", + ) + """ + _response = self._raw_client.list_conversations( + per_page=per_page, starting_after=starting_after, request_options=request_options + ) + return _response.data + + def create_conversation( + self, + *, + from_: CreateConversationRequestFrom, + body: str, + created_at: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Message: + """ + You can create a conversation that has been initiated by a contact (ie. user or lead). + The conversation can be an in-app message only. + + {% admonition type="info" name="Sending for visitors" %} + You can also send a message from a visitor by specifying their `user_id` or `id` value in the `from` field, along with a `type` field value of `contact`. + This visitor will be automatically converted to a contact with a lead role once the conversation is created. + {% /admonition %} + + This will return the Message model that has been created. + + Parameters + ---------- + from_ : CreateConversationRequestFrom + + body : str + The content of the message. HTML is not supported. + + created_at : typing.Optional[int] + The time the conversation was created as a UTC Unix timestamp. If not provided, the current time will be used. This field is only recommneded for migrating past conversations from another source into Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Message + conversation created + + Examples + -------- + from intercom import Intercom + from intercom.unstable.conversations import CreateConversationRequestFrom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.conversations.create_conversation( + from_=CreateConversationRequestFrom( + type="user", + id="6762f11b1bb69f9f2193bba3", + ), + body="Hello there", + ) + """ + _response = self._raw_client.create_conversation( + from_=from_, body=body, created_at=created_at, request_options=request_options + ) + return _response.data + + def retrieve_conversation( + self, + id: int, + *, + display_as: typing.Optional[str] = None, + include_translations: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + + You can fetch the details of a single conversation. + + This will return a single Conversation model with all its conversation parts. + + {% admonition type="warning" name="Hard limit of 500 parts" %} + The maximum number of conversation parts that can be returned via the API is 500. If you have more than that we will return the 500 most recent conversation parts. + {% /admonition %} + + For AI agent conversation metadata, please note that you need to have the agent enabled in your workspace, which is a [paid feature](https://www.intercom.com/help/en/articles/8205718-fin-resolutions#h_97f8c2e671). + + Parameters + ---------- + id : int + The id of the conversation to target + + display_as : typing.Optional[str] + Set to plaintext to retrieve conversation messages in plain text. This affects both the body and subject fields. + + include_translations : typing.Optional[bool] + If set to true, conversation parts will be translated to the detected language of the conversation. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + conversation found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.conversations.retrieve_conversation( + id=1, + display_as="plaintext", + include_translations=True, + ) + """ + _response = self._raw_client.retrieve_conversation( + id, display_as=display_as, include_translations=include_translations, request_options=request_options + ) + return _response.data + + def update_conversation( + self, + id: int, + *, + display_as: typing.Optional[str] = None, + read: typing.Optional[bool] = OMIT, + title: typing.Optional[str] = OMIT, + custom_attributes: typing.Optional[CustomAttributes] = OMIT, + company_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + + You can update an existing conversation. + + {% admonition type="info" name="Replying and other actions" %} + If you want to reply to a coveration or take an action such as assign, unassign, open, close or snooze, take a look at the reply and manage endpoints. + {% /admonition %} + + {% admonition type="info" %} + This endpoint handles both **conversation updates** and **custom object associations**. + + See _`update a conversation with an association to a custom object instance`_ in the request/response examples to see the custom object association format. + {% /admonition %} + + Parameters + ---------- + id : int + The id of the conversation to target + + display_as : typing.Optional[str] + Set to plaintext to retrieve conversation messages in plain text. This affects both the body and subject fields. + + read : typing.Optional[bool] + Mark a conversation as read within Intercom. + + title : typing.Optional[str] + The title given to the conversation + + custom_attributes : typing.Optional[CustomAttributes] + + company_id : typing.Optional[str] + The ID of the company that the conversation is associated with. The unique identifier for the company which is given by Intercom. Set to nil to remove company. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + update a conversation with an association to a custom object instance + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.conversations.update_conversation( + id=1, + display_as="plaintext", + read=True, + title="new conversation title", + custom_attributes={"issue_type": "Billing", "priority": "High"}, + ) + """ + _response = self._raw_client.update_conversation( + id, + display_as=display_as, + read=read, + title=title, + custom_attributes=custom_attributes, + company_id=company_id, + request_options=request_options, + ) + return _response.data + + def delete_conversation( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> ConversationDeleted: + """ + You can delete a single conversation. + + Parameters + ---------- + id : int + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ConversationDeleted + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.conversations.delete_conversation( + id=1, + ) + """ + _response = self._raw_client.delete_conversation(id, request_options=request_options) + return _response.data + + def search_conversations( + self, + *, + query: SearchRequestQuery, + pagination: typing.Optional[StartingAfterPaging] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ConversationList: + """ + You can search for multiple conversations by the value of their attributes in order to fetch exactly which ones you want. + + To search for conversations, you need to send a `POST` request to `https://api.intercom.io/conversations/search`. + + This will accept a query object in the body which will define your filters in order to search for conversations. + {% 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 `20` results per page and maximum is `150`. + 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 %} + + ### 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 + + ### Accepted Fields + + Most keys listed in the conversation 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"`). + The `source.body` field is unique as the search will not be performed against the entire value, but instead against every element of the value separately. For example, when searching for a conversation with a `"I need support"` body - the query should contain a `=` operator with the value `"support"` for such conversation to be returned. A query with a `=` operator and a `"need support"` value will not yield a result. + + | Field | Type | + | :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | + | id | String | + | created_at | Date (UNIX timestamp) | + | updated_at | Date (UNIX timestamp) | + | source.type | String
Accepted fields are `conversation`, `email`, `facebook`, `instagram`, `phone_call`, `phone_switch`, `push`, `sms`, `twitter` and `whatsapp`. | + | source.id | String | + | source.delivered_as | String | + | source.subject | String | + | source.body | String | + | source.author.id | String | + | source.author.type | String | + | source.author.name | String | + | source.author.email | String | + | source.url | String | + | contact_ids | String | + | teammate_ids | String | + | admin_assignee_id | String | + | team_assignee_id | String | + | channel_initiated | String | + | open | Boolean | + | read | Boolean | + | state | String | + | waiting_since | Date (UNIX timestamp) | + | snoozed_until | Date (UNIX timestamp) | + | tag_ids | String | + | priority | String | + | statistics.time_to_assignment | Integer | + | statistics.time_to_admin_reply | Integer | + | statistics.time_to_first_close | Integer | + | statistics.time_to_last_close | Integer | + | statistics.median_time_to_reply | Integer | + | statistics.first_contact_reply_at | Date (UNIX timestamp) | + | statistics.first_assignment_at | Date (UNIX timestamp) | + | statistics.first_admin_reply_at | Date (UNIX timestamp) | + | statistics.first_close_at | Date (UNIX timestamp) | + | statistics.last_assignment_at | Date (UNIX timestamp) | + | statistics.last_assignment_admin_reply_at | Date (UNIX timestamp) | + | statistics.last_contact_reply_at | Date (UNIX timestamp) | + | statistics.last_admin_reply_at | Date (UNIX timestamp) | + | statistics.last_close_at | Date (UNIX timestamp) | + | statistics.last_closed_by_id | String | + | statistics.count_reopens | Integer | + | statistics.count_assignments | Integer | + | statistics.count_conversation_parts | Integer | + | conversation_rating.requested_at | Date (UNIX timestamp) | + | conversation_rating.replied_at | Date (UNIX timestamp) | + | conversation_rating.score | Integer | + | conversation_rating.remark | String | + | conversation_rating.contact_id | String | + | conversation_rating.admin_d | String | + | ai_agent_participated | Boolean | + | ai_agent.resolution_state | String | + | ai_agent.last_answer_type | String | + | ai_agent.rating | Integer | + | ai_agent.rating_remark | String | + | ai_agent.source_type | String | + | ai_agent.source_title | String | + + ### Accepted Operators + + 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 Shortcut for `OR` queries Values most be in Array | + | NIN | All | Not In Shortcut for `OR !` queries Values must be in Array | + | > | Integer Date (UNIX Timestamp) | Greater (or equal) than | + | < | Integer Date (UNIX Timestamp) | Lower (or equal) 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 + ------- + ConversationList + successful + + Examples + -------- + from intercom import Intercom + from intercom.unstable import ( + MultipleFilterSearchRequest, + SingleFilterSearchRequest, + StartingAfterPaging, + ) + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.conversations.search_conversations( + query=MultipleFilterSearchRequest( + operator="AND", + value=[ + SingleFilterSearchRequest( + field="created_at", + operator=">", + value="1306054154", + ) + ], + ), + pagination=StartingAfterPaging( + per_page=5, + ), + ) + """ + _response = self._raw_client.search_conversations( + query=query, pagination=pagination, request_options=request_options + ) + return _response.data + + def reply_conversation( + self, id: str, *, request: ReplyConversationRequestBody, request_options: typing.Optional[RequestOptions] = None + ) -> Conversation: + """ + You can reply to a conversation with a message from an admin or on behalf of a contact, or with a note for admins. + + Parameters + ---------- + id : str + The Intercom provisioned identifier for the conversation or the string "last" to reply to the last part of the conversation + + request : ReplyConversationRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + User last conversation reply + + Examples + -------- + from intercom import Intercom + from intercom.unstable import ContactReplyIntercomUserIdRequest + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.conversations.reply_conversation( + id='123 or "last"', + request=ContactReplyIntercomUserIdRequest( + body="Thanks again :)", + intercom_user_id="6762f1571bb69f9f2193bbbb", + ), + ) + """ + _response = self._raw_client.reply_conversation(id, request=request, request_options=request_options) + return _response.data + + def manage_conversation( + self, + id: str, + *, + request: ManageConversationRequestBody, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + For managing conversations you can: + - Close a conversation + - Snooze a conversation to reopen on a future date + - Open a conversation which is `snoozed` or `closed` + - Assign a conversation to an admin and/or team. + + Parameters + ---------- + id : str + The identifier for the conversation as given by Intercom. + + request : ManageConversationRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + Assign a conversation + + Examples + -------- + from intercom import Intercom + from intercom.unstable.conversations import ManageConversationRequestBody_Close + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.conversations.manage_conversation( + id="123", + request=ManageConversationRequestBody_Close( + admin_id="12345", + ), + ) + """ + _response = self._raw_client.manage_conversation(id, request=request, request_options=request_options) + return _response.data + + def attach_contact_to_conversation( + self, + id: str, + *, + admin_id: typing.Optional[str] = OMIT, + customer: typing.Optional[AttachContactToConversationRequestCustomer] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + You can add participants who are contacts to a conversation, on behalf of either another contact or an admin. + + {% admonition type="warning" name="Contacts without an email" %} + If you add a contact via the email parameter and there is no user/lead found on that workspace with he given email, then we will create a new contact with `role` set to `lead`. + {% /admonition %} + + Parameters + ---------- + id : str + The identifier for the conversation as given by Intercom. + + admin_id : typing.Optional[str] + The `id` of the admin who is adding the new participant. + + customer : typing.Optional[AttachContactToConversationRequestCustomer] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + Attach a contact to a conversation + + Examples + -------- + from intercom import Intercom + from intercom.unstable.conversations import ( + AttachContactToConversationRequestCustomerIntercomUserId, + ) + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.conversations.attach_contact_to_conversation( + id="123", + admin_id="12345", + customer=AttachContactToConversationRequestCustomerIntercomUserId( + intercom_user_id="6762f19b1bb69f9f2193bbd4", + ), + ) + """ + _response = self._raw_client.attach_contact_to_conversation( + id, admin_id=admin_id, customer=customer, request_options=request_options + ) + return _response.data + + def detach_contact_from_conversation( + self, + conversation_id: str, + contact_id: str, + *, + admin_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + You can add participants who are contacts to a conversation, on behalf of either another contact or an admin. + + {% admonition type="warning" name="Contacts without an email" %} + If you add a contact via the email parameter and there is no user/lead found on that workspace with he given email, then we will create a new contact with `role` set to `lead`. + {% /admonition %} + + Parameters + ---------- + conversation_id : str + The identifier for the conversation as given by Intercom. + + contact_id : str + The identifier for the contact as given by Intercom. + + admin_id : str + The `id` of the admin who is performing the action. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + Detach a contact from a group conversation + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.conversations.detach_contact_from_conversation( + conversation_id="123", + contact_id="123", + admin_id="5017690", + ) + """ + _response = self._raw_client.detach_contact_from_conversation( + conversation_id, contact_id, admin_id=admin_id, request_options=request_options + ) + return _response.data + + def redact_conversation( + self, *, request: RedactConversationRequest, request_options: typing.Optional[RequestOptions] = None + ) -> Conversation: + """ + You can redact a conversation part or the source message of a conversation (as seen in the source object). + + {% admonition type="info" name="Redacting parts and messages" %} + If you are redacting a conversation part, it must have a `body`. If you are redacting a source message, it must have been created by a contact. We will return a `conversation_part_not_redactable` error if these criteria are not met. + {% /admonition %} + + Parameters + ---------- + request : RedactConversationRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + Redact a conversation part + + Examples + -------- + from intercom import Intercom + from intercom.unstable import RedactConversationRequest_ConversationPart + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.conversations.redact_conversation( + request=RedactConversationRequest_ConversationPart( + conversation_id="19894788788", + conversation_part_id="19381789428", + ), + ) + """ + _response = self._raw_client.redact_conversation(request=request, request_options=request_options) + return _response.data + + def convert_conversation_to_ticket( + self, + id: int, + *, + ticket_type_id: str, + attributes: typing.Optional[TicketRequestCustomAttributes] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[Ticket]: + """ + You can convert a conversation to a ticket. + + Parameters + ---------- + id : int + The id of the conversation to target + + ticket_type_id : str + The ID of the type of ticket you want to convert the conversation to + + attributes : typing.Optional[TicketRequestCustomAttributes] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Ticket] + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.conversations.convert_conversation_to_ticket( + id=1, + ticket_type_id="53", + ) + """ + _response = self._raw_client.convert_conversation_to_ticket( + id, ticket_type_id=ticket_type_id, attributes=attributes, request_options=request_options + ) + return _response.data + + +class AsyncConversationsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawConversationsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawConversationsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawConversationsClient + """ + return self._raw_client + + async def list_conversations( + self, + *, + per_page: typing.Optional[int] = None, + starting_after: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> ConversationList: + """ + You can fetch a list of all conversations. + + You can optionally request the result page size and the cursor to start after to fetch the result. + {% admonition type="warning" name="Pagination" %} + You can use pagination to limit the number of results returned. The default is `20` 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 + ---------- + per_page : typing.Optional[int] + How many results per page + + starting_after : typing.Optional[str] + String used to get the next page of conversations. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ConversationList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.conversations.list_conversations( + per_page=1, + starting_after="starting_after", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_conversations( + per_page=per_page, starting_after=starting_after, request_options=request_options + ) + return _response.data + + async def create_conversation( + self, + *, + from_: CreateConversationRequestFrom, + body: str, + created_at: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Message: + """ + You can create a conversation that has been initiated by a contact (ie. user or lead). + The conversation can be an in-app message only. + + {% admonition type="info" name="Sending for visitors" %} + You can also send a message from a visitor by specifying their `user_id` or `id` value in the `from` field, along with a `type` field value of `contact`. + This visitor will be automatically converted to a contact with a lead role once the conversation is created. + {% /admonition %} + + This will return the Message model that has been created. + + Parameters + ---------- + from_ : CreateConversationRequestFrom + + body : str + The content of the message. HTML is not supported. + + created_at : typing.Optional[int] + The time the conversation was created as a UTC Unix timestamp. If not provided, the current time will be used. This field is only recommneded for migrating past conversations from another source into Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Message + conversation created + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.unstable.conversations import CreateConversationRequestFrom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.conversations.create_conversation( + from_=CreateConversationRequestFrom( + type="user", + id="6762f11b1bb69f9f2193bba3", + ), + body="Hello there", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_conversation( + from_=from_, body=body, created_at=created_at, request_options=request_options + ) + return _response.data + + async def retrieve_conversation( + self, + id: int, + *, + display_as: typing.Optional[str] = None, + include_translations: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + + You can fetch the details of a single conversation. + + This will return a single Conversation model with all its conversation parts. + + {% admonition type="warning" name="Hard limit of 500 parts" %} + The maximum number of conversation parts that can be returned via the API is 500. If you have more than that we will return the 500 most recent conversation parts. + {% /admonition %} + + For AI agent conversation metadata, please note that you need to have the agent enabled in your workspace, which is a [paid feature](https://www.intercom.com/help/en/articles/8205718-fin-resolutions#h_97f8c2e671). + + Parameters + ---------- + id : int + The id of the conversation to target + + display_as : typing.Optional[str] + Set to plaintext to retrieve conversation messages in plain text. This affects both the body and subject fields. + + include_translations : typing.Optional[bool] + If set to true, conversation parts will be translated to the detected language of the conversation. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + conversation found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.conversations.retrieve_conversation( + id=1, + display_as="plaintext", + include_translations=True, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.retrieve_conversation( + id, display_as=display_as, include_translations=include_translations, request_options=request_options + ) + return _response.data + + async def update_conversation( + self, + id: int, + *, + display_as: typing.Optional[str] = None, + read: typing.Optional[bool] = OMIT, + title: typing.Optional[str] = OMIT, + custom_attributes: typing.Optional[CustomAttributes] = OMIT, + company_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + + You can update an existing conversation. + + {% admonition type="info" name="Replying and other actions" %} + If you want to reply to a coveration or take an action such as assign, unassign, open, close or snooze, take a look at the reply and manage endpoints. + {% /admonition %} + + {% admonition type="info" %} + This endpoint handles both **conversation updates** and **custom object associations**. + + See _`update a conversation with an association to a custom object instance`_ in the request/response examples to see the custom object association format. + {% /admonition %} + + Parameters + ---------- + id : int + The id of the conversation to target + + display_as : typing.Optional[str] + Set to plaintext to retrieve conversation messages in plain text. This affects both the body and subject fields. + + read : typing.Optional[bool] + Mark a conversation as read within Intercom. + + title : typing.Optional[str] + The title given to the conversation + + custom_attributes : typing.Optional[CustomAttributes] + + company_id : typing.Optional[str] + The ID of the company that the conversation is associated with. The unique identifier for the company which is given by Intercom. Set to nil to remove company. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + update a conversation with an association to a custom object instance + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.conversations.update_conversation( + id=1, + display_as="plaintext", + read=True, + title="new conversation title", + custom_attributes={"issue_type": "Billing", "priority": "High"}, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update_conversation( + id, + display_as=display_as, + read=read, + title=title, + custom_attributes=custom_attributes, + company_id=company_id, + request_options=request_options, + ) + return _response.data + + async def delete_conversation( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> ConversationDeleted: + """ + You can delete a single conversation. + + Parameters + ---------- + id : int + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + ConversationDeleted + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.conversations.delete_conversation( + id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_conversation(id, request_options=request_options) + return _response.data + + async def search_conversations( + self, + *, + query: SearchRequestQuery, + pagination: typing.Optional[StartingAfterPaging] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> ConversationList: + """ + You can search for multiple conversations by the value of their attributes in order to fetch exactly which ones you want. + + To search for conversations, you need to send a `POST` request to `https://api.intercom.io/conversations/search`. + + This will accept a query object in the body which will define your filters in order to search for conversations. + {% 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 `20` results per page and maximum is `150`. + 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 %} + + ### 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 + + ### Accepted Fields + + Most keys listed in the conversation 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"`). + The `source.body` field is unique as the search will not be performed against the entire value, but instead against every element of the value separately. For example, when searching for a conversation with a `"I need support"` body - the query should contain a `=` operator with the value `"support"` for such conversation to be returned. A query with a `=` operator and a `"need support"` value will not yield a result. + + | Field | Type | + | :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | + | id | String | + | created_at | Date (UNIX timestamp) | + | updated_at | Date (UNIX timestamp) | + | source.type | String
Accepted fields are `conversation`, `email`, `facebook`, `instagram`, `phone_call`, `phone_switch`, `push`, `sms`, `twitter` and `whatsapp`. | + | source.id | String | + | source.delivered_as | String | + | source.subject | String | + | source.body | String | + | source.author.id | String | + | source.author.type | String | + | source.author.name | String | + | source.author.email | String | + | source.url | String | + | contact_ids | String | + | teammate_ids | String | + | admin_assignee_id | String | + | team_assignee_id | String | + | channel_initiated | String | + | open | Boolean | + | read | Boolean | + | state | String | + | waiting_since | Date (UNIX timestamp) | + | snoozed_until | Date (UNIX timestamp) | + | tag_ids | String | + | priority | String | + | statistics.time_to_assignment | Integer | + | statistics.time_to_admin_reply | Integer | + | statistics.time_to_first_close | Integer | + | statistics.time_to_last_close | Integer | + | statistics.median_time_to_reply | Integer | + | statistics.first_contact_reply_at | Date (UNIX timestamp) | + | statistics.first_assignment_at | Date (UNIX timestamp) | + | statistics.first_admin_reply_at | Date (UNIX timestamp) | + | statistics.first_close_at | Date (UNIX timestamp) | + | statistics.last_assignment_at | Date (UNIX timestamp) | + | statistics.last_assignment_admin_reply_at | Date (UNIX timestamp) | + | statistics.last_contact_reply_at | Date (UNIX timestamp) | + | statistics.last_admin_reply_at | Date (UNIX timestamp) | + | statistics.last_close_at | Date (UNIX timestamp) | + | statistics.last_closed_by_id | String | + | statistics.count_reopens | Integer | + | statistics.count_assignments | Integer | + | statistics.count_conversation_parts | Integer | + | conversation_rating.requested_at | Date (UNIX timestamp) | + | conversation_rating.replied_at | Date (UNIX timestamp) | + | conversation_rating.score | Integer | + | conversation_rating.remark | String | + | conversation_rating.contact_id | String | + | conversation_rating.admin_d | String | + | ai_agent_participated | Boolean | + | ai_agent.resolution_state | String | + | ai_agent.last_answer_type | String | + | ai_agent.rating | Integer | + | ai_agent.rating_remark | String | + | ai_agent.source_type | String | + | ai_agent.source_title | String | + + ### Accepted Operators + + 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 Shortcut for `OR` queries Values most be in Array | + | NIN | All | Not In Shortcut for `OR !` queries Values must be in Array | + | > | Integer Date (UNIX Timestamp) | Greater (or equal) than | + | < | Integer Date (UNIX Timestamp) | Lower (or equal) 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 + ------- + ConversationList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.unstable import ( + MultipleFilterSearchRequest, + SingleFilterSearchRequest, + StartingAfterPaging, + ) + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.conversations.search_conversations( + query=MultipleFilterSearchRequest( + operator="AND", + value=[ + SingleFilterSearchRequest( + field="created_at", + operator=">", + value="1306054154", + ) + ], + ), + pagination=StartingAfterPaging( + per_page=5, + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.search_conversations( + query=query, pagination=pagination, request_options=request_options + ) + return _response.data + + async def reply_conversation( + self, id: str, *, request: ReplyConversationRequestBody, request_options: typing.Optional[RequestOptions] = None + ) -> Conversation: + """ + You can reply to a conversation with a message from an admin or on behalf of a contact, or with a note for admins. + + Parameters + ---------- + id : str + The Intercom provisioned identifier for the conversation or the string "last" to reply to the last part of the conversation + + request : ReplyConversationRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + User last conversation reply + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.unstable import ContactReplyIntercomUserIdRequest + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.conversations.reply_conversation( + id='123 or "last"', + request=ContactReplyIntercomUserIdRequest( + body="Thanks again :)", + intercom_user_id="6762f1571bb69f9f2193bbbb", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.reply_conversation(id, request=request, request_options=request_options) + return _response.data + + async def manage_conversation( + self, + id: str, + *, + request: ManageConversationRequestBody, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + For managing conversations you can: + - Close a conversation + - Snooze a conversation to reopen on a future date + - Open a conversation which is `snoozed` or `closed` + - Assign a conversation to an admin and/or team. + + Parameters + ---------- + id : str + The identifier for the conversation as given by Intercom. + + request : ManageConversationRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + Assign a conversation + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.unstable.conversations import ManageConversationRequestBody_Close + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.conversations.manage_conversation( + id="123", + request=ManageConversationRequestBody_Close( + admin_id="12345", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.manage_conversation(id, request=request, request_options=request_options) + return _response.data + + async def attach_contact_to_conversation( + self, + id: str, + *, + admin_id: typing.Optional[str] = OMIT, + customer: typing.Optional[AttachContactToConversationRequestCustomer] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + You can add participants who are contacts to a conversation, on behalf of either another contact or an admin. + + {% admonition type="warning" name="Contacts without an email" %} + If you add a contact via the email parameter and there is no user/lead found on that workspace with he given email, then we will create a new contact with `role` set to `lead`. + {% /admonition %} + + Parameters + ---------- + id : str + The identifier for the conversation as given by Intercom. + + admin_id : typing.Optional[str] + The `id` of the admin who is adding the new participant. + + customer : typing.Optional[AttachContactToConversationRequestCustomer] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + Attach a contact to a conversation + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.unstable.conversations import ( + AttachContactToConversationRequestCustomerIntercomUserId, + ) + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.conversations.attach_contact_to_conversation( + id="123", + admin_id="12345", + customer=AttachContactToConversationRequestCustomerIntercomUserId( + intercom_user_id="6762f19b1bb69f9f2193bbd4", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.attach_contact_to_conversation( + id, admin_id=admin_id, customer=customer, request_options=request_options + ) + return _response.data + + async def detach_contact_from_conversation( + self, + conversation_id: str, + contact_id: str, + *, + admin_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> Conversation: + """ + You can add participants who are contacts to a conversation, on behalf of either another contact or an admin. + + {% admonition type="warning" name="Contacts without an email" %} + If you add a contact via the email parameter and there is no user/lead found on that workspace with he given email, then we will create a new contact with `role` set to `lead`. + {% /admonition %} + + Parameters + ---------- + conversation_id : str + The identifier for the conversation as given by Intercom. + + contact_id : str + The identifier for the contact as given by Intercom. + + admin_id : str + The `id` of the admin who is performing the action. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + Detach a contact from a group conversation + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.conversations.detach_contact_from_conversation( + conversation_id="123", + contact_id="123", + admin_id="5017690", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.detach_contact_from_conversation( + conversation_id, contact_id, admin_id=admin_id, request_options=request_options + ) + return _response.data + + async def redact_conversation( + self, *, request: RedactConversationRequest, request_options: typing.Optional[RequestOptions] = None + ) -> Conversation: + """ + You can redact a conversation part or the source message of a conversation (as seen in the source object). + + {% admonition type="info" name="Redacting parts and messages" %} + If you are redacting a conversation part, it must have a `body`. If you are redacting a source message, it must have been created by a contact. We will return a `conversation_part_not_redactable` error if these criteria are not met. + {% /admonition %} + + Parameters + ---------- + request : RedactConversationRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Conversation + Redact a conversation part + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.unstable import RedactConversationRequest_ConversationPart + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.conversations.redact_conversation( + request=RedactConversationRequest_ConversationPart( + conversation_id="19894788788", + conversation_part_id="19381789428", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.redact_conversation(request=request, request_options=request_options) + return _response.data + + async def convert_conversation_to_ticket( + self, + id: int, + *, + ticket_type_id: str, + attributes: typing.Optional[TicketRequestCustomAttributes] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[Ticket]: + """ + You can convert a conversation to a ticket. + + Parameters + ---------- + id : int + The id of the conversation to target + + ticket_type_id : str + The ID of the type of ticket you want to convert the conversation to + + attributes : typing.Optional[TicketRequestCustomAttributes] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Ticket] + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.conversations.convert_conversation_to_ticket( + id=1, + ticket_type_id="53", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.convert_conversation_to_ticket( + id, ticket_type_id=ticket_type_id, attributes=attributes, request_options=request_options + ) + return _response.data diff --git a/src/intercom/unstable/conversations/raw_client.py b/src/intercom/unstable/conversations/raw_client.py new file mode 100644 index 00000000..7931f53a --- /dev/null +++ b/src/intercom/unstable/conversations/raw_client.py @@ -0,0 +1,2293 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.serialization import convert_and_respect_annotation_metadata +from ...core.unchecked_base_model import construct_type +from ..errors.bad_request_error import BadRequestError +from ..errors.forbidden_error import ForbiddenError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..errors.unprocessable_entity_error import UnprocessableEntityError +from ..messages.types.message import Message +from ..tickets.types.ticket import Ticket +from ..types.conversation_deleted import ConversationDeleted +from ..types.conversation_list import ConversationList +from ..types.custom_attributes import CustomAttributes +from ..types.error import Error +from ..types.redact_conversation_request import RedactConversationRequest +from ..types.reply_conversation_request_body import ReplyConversationRequestBody +from ..types.search_request_query import SearchRequestQuery +from ..types.starting_after_paging import StartingAfterPaging +from ..types.ticket_request_custom_attributes import TicketRequestCustomAttributes +from .types.attach_contact_to_conversation_request_customer import AttachContactToConversationRequestCustomer +from .types.conversation import Conversation +from .types.create_conversation_request_from import CreateConversationRequestFrom +from .types.manage_conversation_request_body import ManageConversationRequestBody + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawConversationsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_conversations( + self, + *, + per_page: typing.Optional[int] = None, + starting_after: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ConversationList]: + """ + You can fetch a list of all conversations. + + You can optionally request the result page size and the cursor to start after to fetch the result. + {% admonition type="warning" name="Pagination" %} + You can use pagination to limit the number of results returned. The default is `20` 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 + ---------- + per_page : typing.Optional[int] + How many results per page + + starting_after : typing.Optional[str] + String used to get the next page of conversations. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ConversationList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "conversations", + method="GET", + params={ + "per_page": per_page, + "starting_after": starting_after, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ConversationList, + construct_type( + type_=ConversationList, # 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 == 403: + raise ForbiddenError( + 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_conversation( + self, + *, + from_: CreateConversationRequestFrom, + body: str, + created_at: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Message]: + """ + You can create a conversation that has been initiated by a contact (ie. user or lead). + The conversation can be an in-app message only. + + {% admonition type="info" name="Sending for visitors" %} + You can also send a message from a visitor by specifying their `user_id` or `id` value in the `from` field, along with a `type` field value of `contact`. + This visitor will be automatically converted to a contact with a lead role once the conversation is created. + {% /admonition %} + + This will return the Message model that has been created. + + Parameters + ---------- + from_ : CreateConversationRequestFrom + + body : str + The content of the message. HTML is not supported. + + created_at : typing.Optional[int] + The time the conversation was created as a UTC Unix timestamp. If not provided, the current time will be used. This field is only recommneded for migrating past conversations from another source into Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Message] + conversation created + """ + _response = self._client_wrapper.httpx_client.request( + "conversations", + method="POST", + json={ + "from": convert_and_respect_annotation_metadata( + object_=from_, annotation=CreateConversationRequestFrom, direction="write" + ), + "body": body, + "created_at": created_at, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Message, + construct_type( + type_=Message, # 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 == 403: + raise ForbiddenError( + 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 retrieve_conversation( + self, + id: int, + *, + display_as: typing.Optional[str] = None, + include_translations: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Conversation]: + """ + + You can fetch the details of a single conversation. + + This will return a single Conversation model with all its conversation parts. + + {% admonition type="warning" name="Hard limit of 500 parts" %} + The maximum number of conversation parts that can be returned via the API is 500. If you have more than that we will return the 500 most recent conversation parts. + {% /admonition %} + + For AI agent conversation metadata, please note that you need to have the agent enabled in your workspace, which is a [paid feature](https://www.intercom.com/help/en/articles/8205718-fin-resolutions#h_97f8c2e671). + + Parameters + ---------- + id : int + The id of the conversation to target + + display_as : typing.Optional[str] + Set to plaintext to retrieve conversation messages in plain text. This affects both the body and subject fields. + + include_translations : typing.Optional[bool] + If set to true, conversation parts will be translated to the detected language of the conversation. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Conversation] + conversation found + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(id)}", + method="GET", + params={ + "display_as": display_as, + "include_translations": include_translations, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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 update_conversation( + self, + id: int, + *, + display_as: typing.Optional[str] = None, + read: typing.Optional[bool] = OMIT, + title: typing.Optional[str] = OMIT, + custom_attributes: typing.Optional[CustomAttributes] = OMIT, + company_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Conversation]: + """ + + You can update an existing conversation. + + {% admonition type="info" name="Replying and other actions" %} + If you want to reply to a coveration or take an action such as assign, unassign, open, close or snooze, take a look at the reply and manage endpoints. + {% /admonition %} + + {% admonition type="info" %} + This endpoint handles both **conversation updates** and **custom object associations**. + + See _`update a conversation with an association to a custom object instance`_ in the request/response examples to see the custom object association format. + {% /admonition %} + + Parameters + ---------- + id : int + The id of the conversation to target + + display_as : typing.Optional[str] + Set to plaintext to retrieve conversation messages in plain text. This affects both the body and subject fields. + + read : typing.Optional[bool] + Mark a conversation as read within Intercom. + + title : typing.Optional[str] + The title given to the conversation + + custom_attributes : typing.Optional[CustomAttributes] + + company_id : typing.Optional[str] + The ID of the company that the conversation is associated with. The unique identifier for the company which is given by Intercom. Set to nil to remove company. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Conversation] + update a conversation with an association to a custom object instance + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(id)}", + method="PUT", + params={ + "display_as": display_as, + }, + json={ + "read": read, + "title": title, + "custom_attributes": convert_and_respect_annotation_metadata( + object_=custom_attributes, annotation=CustomAttributes, direction="write" + ), + "company_id": company_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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 delete_conversation( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[ConversationDeleted]: + """ + You can delete a single conversation. + + Parameters + ---------- + id : int + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[ConversationDeleted] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ConversationDeleted, + construct_type( + type_=ConversationDeleted, # 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 == 403: + raise ForbiddenError( + 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_conversations( + self, + *, + query: SearchRequestQuery, + pagination: typing.Optional[StartingAfterPaging] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[ConversationList]: + """ + You can search for multiple conversations by the value of their attributes in order to fetch exactly which ones you want. + + To search for conversations, you need to send a `POST` request to `https://api.intercom.io/conversations/search`. + + This will accept a query object in the body which will define your filters in order to search for conversations. + {% 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 `20` results per page and maximum is `150`. + 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 %} + + ### 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 + + ### Accepted Fields + + Most keys listed in the conversation 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"`). + The `source.body` field is unique as the search will not be performed against the entire value, but instead against every element of the value separately. For example, when searching for a conversation with a `"I need support"` body - the query should contain a `=` operator with the value `"support"` for such conversation to be returned. A query with a `=` operator and a `"need support"` value will not yield a result. + + | Field | Type | + | :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | + | id | String | + | created_at | Date (UNIX timestamp) | + | updated_at | Date (UNIX timestamp) | + | source.type | String
Accepted fields are `conversation`, `email`, `facebook`, `instagram`, `phone_call`, `phone_switch`, `push`, `sms`, `twitter` and `whatsapp`. | + | source.id | String | + | source.delivered_as | String | + | source.subject | String | + | source.body | String | + | source.author.id | String | + | source.author.type | String | + | source.author.name | String | + | source.author.email | String | + | source.url | String | + | contact_ids | String | + | teammate_ids | String | + | admin_assignee_id | String | + | team_assignee_id | String | + | channel_initiated | String | + | open | Boolean | + | read | Boolean | + | state | String | + | waiting_since | Date (UNIX timestamp) | + | snoozed_until | Date (UNIX timestamp) | + | tag_ids | String | + | priority | String | + | statistics.time_to_assignment | Integer | + | statistics.time_to_admin_reply | Integer | + | statistics.time_to_first_close | Integer | + | statistics.time_to_last_close | Integer | + | statistics.median_time_to_reply | Integer | + | statistics.first_contact_reply_at | Date (UNIX timestamp) | + | statistics.first_assignment_at | Date (UNIX timestamp) | + | statistics.first_admin_reply_at | Date (UNIX timestamp) | + | statistics.first_close_at | Date (UNIX timestamp) | + | statistics.last_assignment_at | Date (UNIX timestamp) | + | statistics.last_assignment_admin_reply_at | Date (UNIX timestamp) | + | statistics.last_contact_reply_at | Date (UNIX timestamp) | + | statistics.last_admin_reply_at | Date (UNIX timestamp) | + | statistics.last_close_at | Date (UNIX timestamp) | + | statistics.last_closed_by_id | String | + | statistics.count_reopens | Integer | + | statistics.count_assignments | Integer | + | statistics.count_conversation_parts | Integer | + | conversation_rating.requested_at | Date (UNIX timestamp) | + | conversation_rating.replied_at | Date (UNIX timestamp) | + | conversation_rating.score | Integer | + | conversation_rating.remark | String | + | conversation_rating.contact_id | String | + | conversation_rating.admin_d | String | + | ai_agent_participated | Boolean | + | ai_agent.resolution_state | String | + | ai_agent.last_answer_type | String | + | ai_agent.rating | Integer | + | ai_agent.rating_remark | String | + | ai_agent.source_type | String | + | ai_agent.source_title | String | + + ### Accepted Operators + + 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 Shortcut for `OR` queries Values most be in Array | + | NIN | All | Not In Shortcut for `OR !` queries Values must be in Array | + | > | Integer Date (UNIX Timestamp) | Greater (or equal) than | + | < | Integer Date (UNIX Timestamp) | Lower (or equal) 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 + ------- + HttpResponse[ConversationList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "conversations/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: + _data = typing.cast( + ConversationList, + construct_type( + type_=ConversationList, # 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 reply_conversation( + self, id: str, *, request: ReplyConversationRequestBody, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Conversation]: + """ + You can reply to a conversation with a message from an admin or on behalf of a contact, or with a note for admins. + + Parameters + ---------- + id : str + The Intercom provisioned identifier for the conversation or the string "last" to reply to the last part of the conversation + + request : ReplyConversationRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Conversation] + User last conversation reply + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(id)}/reply", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=ReplyConversationRequestBody, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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 manage_conversation( + self, + id: str, + *, + request: ManageConversationRequestBody, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Conversation]: + """ + For managing conversations you can: + - Close a conversation + - Snooze a conversation to reopen on a future date + - Open a conversation which is `snoozed` or `closed` + - Assign a conversation to an admin and/or team. + + Parameters + ---------- + id : str + The identifier for the conversation as given by Intercom. + + request : ManageConversationRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Conversation] + Assign a conversation + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(id)}/parts", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=ManageConversationRequestBody, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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_contact_to_conversation( + self, + id: str, + *, + admin_id: typing.Optional[str] = OMIT, + customer: typing.Optional[AttachContactToConversationRequestCustomer] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Conversation]: + """ + You can add participants who are contacts to a conversation, on behalf of either another contact or an admin. + + {% admonition type="warning" name="Contacts without an email" %} + If you add a contact via the email parameter and there is no user/lead found on that workspace with he given email, then we will create a new contact with `role` set to `lead`. + {% /admonition %} + + Parameters + ---------- + id : str + The identifier for the conversation as given by Intercom. + + admin_id : typing.Optional[str] + The `id` of the admin who is adding the new participant. + + customer : typing.Optional[AttachContactToConversationRequestCustomer] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Conversation] + Attach a contact to a conversation + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(id)}/customers", + method="POST", + json={ + "admin_id": admin_id, + "customer": convert_and_respect_annotation_metadata( + object_=customer, annotation=AttachContactToConversationRequestCustomer, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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_contact_from_conversation( + self, + conversation_id: str, + contact_id: str, + *, + admin_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Conversation]: + """ + You can add participants who are contacts to a conversation, on behalf of either another contact or an admin. + + {% admonition type="warning" name="Contacts without an email" %} + If you add a contact via the email parameter and there is no user/lead found on that workspace with he given email, then we will create a new contact with `role` set to `lead`. + {% /admonition %} + + Parameters + ---------- + conversation_id : str + The identifier for the conversation as given by Intercom. + + contact_id : str + The identifier for the contact as given by Intercom. + + admin_id : str + The `id` of the admin who is performing the action. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Conversation] + Detach a contact from a group conversation + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/customers/{jsonable_encoder(contact_id)}", + method="DELETE", + json={ + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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 redact_conversation( + self, *, request: RedactConversationRequest, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Conversation]: + """ + You can redact a conversation part or the source message of a conversation (as seen in the source object). + + {% admonition type="info" name="Redacting parts and messages" %} + If you are redacting a conversation part, it must have a `body`. If you are redacting a source message, it must have been created by a contact. We will return a `conversation_part_not_redactable` error if these criteria are not met. + {% /admonition %} + + Parameters + ---------- + request : RedactConversationRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Conversation] + Redact a conversation part + """ + _response = self._client_wrapper.httpx_client.request( + "conversations/redact", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=RedactConversationRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 convert_conversation_to_ticket( + self, + id: int, + *, + ticket_type_id: str, + attributes: typing.Optional[TicketRequestCustomAttributes] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[typing.Optional[Ticket]]: + """ + You can convert a conversation to a ticket. + + Parameters + ---------- + id : int + The id of the conversation to target + + ticket_type_id : str + The ID of the type of ticket you want to convert the conversation to + + attributes : typing.Optional[TicketRequestCustomAttributes] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[Ticket]] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(id)}/convert", + method="POST", + json={ + "ticket_type_id": ticket_type_id, + "attributes": convert_and_respect_annotation_metadata( + object_=attributes, annotation=TicketRequestCustomAttributes, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Ticket], + construct_type( + type_=typing.Optional[Ticket], # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + 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) + + +class AsyncRawConversationsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_conversations( + self, + *, + per_page: typing.Optional[int] = None, + starting_after: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ConversationList]: + """ + You can fetch a list of all conversations. + + You can optionally request the result page size and the cursor to start after to fetch the result. + {% admonition type="warning" name="Pagination" %} + You can use pagination to limit the number of results returned. The default is `20` 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 + ---------- + per_page : typing.Optional[int] + How many results per page + + starting_after : typing.Optional[str] + String used to get the next page of conversations. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ConversationList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "conversations", + method="GET", + params={ + "per_page": per_page, + "starting_after": starting_after, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ConversationList, + construct_type( + type_=ConversationList, # 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 == 403: + raise ForbiddenError( + 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_conversation( + self, + *, + from_: CreateConversationRequestFrom, + body: str, + created_at: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Message]: + """ + You can create a conversation that has been initiated by a contact (ie. user or lead). + The conversation can be an in-app message only. + + {% admonition type="info" name="Sending for visitors" %} + You can also send a message from a visitor by specifying their `user_id` or `id` value in the `from` field, along with a `type` field value of `contact`. + This visitor will be automatically converted to a contact with a lead role once the conversation is created. + {% /admonition %} + + This will return the Message model that has been created. + + Parameters + ---------- + from_ : CreateConversationRequestFrom + + body : str + The content of the message. HTML is not supported. + + created_at : typing.Optional[int] + The time the conversation was created as a UTC Unix timestamp. If not provided, the current time will be used. This field is only recommneded for migrating past conversations from another source into Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Message] + conversation created + """ + _response = await self._client_wrapper.httpx_client.request( + "conversations", + method="POST", + json={ + "from": convert_and_respect_annotation_metadata( + object_=from_, annotation=CreateConversationRequestFrom, direction="write" + ), + "body": body, + "created_at": created_at, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Message, + construct_type( + type_=Message, # 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 == 403: + raise ForbiddenError( + 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 retrieve_conversation( + self, + id: int, + *, + display_as: typing.Optional[str] = None, + include_translations: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Conversation]: + """ + + You can fetch the details of a single conversation. + + This will return a single Conversation model with all its conversation parts. + + {% admonition type="warning" name="Hard limit of 500 parts" %} + The maximum number of conversation parts that can be returned via the API is 500. If you have more than that we will return the 500 most recent conversation parts. + {% /admonition %} + + For AI agent conversation metadata, please note that you need to have the agent enabled in your workspace, which is a [paid feature](https://www.intercom.com/help/en/articles/8205718-fin-resolutions#h_97f8c2e671). + + Parameters + ---------- + id : int + The id of the conversation to target + + display_as : typing.Optional[str] + Set to plaintext to retrieve conversation messages in plain text. This affects both the body and subject fields. + + include_translations : typing.Optional[bool] + If set to true, conversation parts will be translated to the detected language of the conversation. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Conversation] + conversation found + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(id)}", + method="GET", + params={ + "display_as": display_as, + "include_translations": include_translations, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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 update_conversation( + self, + id: int, + *, + display_as: typing.Optional[str] = None, + read: typing.Optional[bool] = OMIT, + title: typing.Optional[str] = OMIT, + custom_attributes: typing.Optional[CustomAttributes] = OMIT, + company_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Conversation]: + """ + + You can update an existing conversation. + + {% admonition type="info" name="Replying and other actions" %} + If you want to reply to a coveration or take an action such as assign, unassign, open, close or snooze, take a look at the reply and manage endpoints. + {% /admonition %} + + {% admonition type="info" %} + This endpoint handles both **conversation updates** and **custom object associations**. + + See _`update a conversation with an association to a custom object instance`_ in the request/response examples to see the custom object association format. + {% /admonition %} + + Parameters + ---------- + id : int + The id of the conversation to target + + display_as : typing.Optional[str] + Set to plaintext to retrieve conversation messages in plain text. This affects both the body and subject fields. + + read : typing.Optional[bool] + Mark a conversation as read within Intercom. + + title : typing.Optional[str] + The title given to the conversation + + custom_attributes : typing.Optional[CustomAttributes] + + company_id : typing.Optional[str] + The ID of the company that the conversation is associated with. The unique identifier for the company which is given by Intercom. Set to nil to remove company. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Conversation] + update a conversation with an association to a custom object instance + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(id)}", + method="PUT", + params={ + "display_as": display_as, + }, + json={ + "read": read, + "title": title, + "custom_attributes": convert_and_respect_annotation_metadata( + object_=custom_attributes, annotation=CustomAttributes, direction="write" + ), + "company_id": company_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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 delete_conversation( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[ConversationDeleted]: + """ + You can delete a single conversation. + + Parameters + ---------- + id : int + id + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[ConversationDeleted] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + ConversationDeleted, + construct_type( + type_=ConversationDeleted, # 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 == 403: + raise ForbiddenError( + 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_conversations( + self, + *, + query: SearchRequestQuery, + pagination: typing.Optional[StartingAfterPaging] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[ConversationList]: + """ + You can search for multiple conversations by the value of their attributes in order to fetch exactly which ones you want. + + To search for conversations, you need to send a `POST` request to `https://api.intercom.io/conversations/search`. + + This will accept a query object in the body which will define your filters in order to search for conversations. + {% 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 `20` results per page and maximum is `150`. + 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 %} + + ### 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 + + ### Accepted Fields + + Most keys listed in the conversation 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"`). + The `source.body` field is unique as the search will not be performed against the entire value, but instead against every element of the value separately. For example, when searching for a conversation with a `"I need support"` body - the query should contain a `=` operator with the value `"support"` for such conversation to be returned. A query with a `=` operator and a `"need support"` value will not yield a result. + + | Field | Type | + | :---------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | + | id | String | + | created_at | Date (UNIX timestamp) | + | updated_at | Date (UNIX timestamp) | + | source.type | String
Accepted fields are `conversation`, `email`, `facebook`, `instagram`, `phone_call`, `phone_switch`, `push`, `sms`, `twitter` and `whatsapp`. | + | source.id | String | + | source.delivered_as | String | + | source.subject | String | + | source.body | String | + | source.author.id | String | + | source.author.type | String | + | source.author.name | String | + | source.author.email | String | + | source.url | String | + | contact_ids | String | + | teammate_ids | String | + | admin_assignee_id | String | + | team_assignee_id | String | + | channel_initiated | String | + | open | Boolean | + | read | Boolean | + | state | String | + | waiting_since | Date (UNIX timestamp) | + | snoozed_until | Date (UNIX timestamp) | + | tag_ids | String | + | priority | String | + | statistics.time_to_assignment | Integer | + | statistics.time_to_admin_reply | Integer | + | statistics.time_to_first_close | Integer | + | statistics.time_to_last_close | Integer | + | statistics.median_time_to_reply | Integer | + | statistics.first_contact_reply_at | Date (UNIX timestamp) | + | statistics.first_assignment_at | Date (UNIX timestamp) | + | statistics.first_admin_reply_at | Date (UNIX timestamp) | + | statistics.first_close_at | Date (UNIX timestamp) | + | statistics.last_assignment_at | Date (UNIX timestamp) | + | statistics.last_assignment_admin_reply_at | Date (UNIX timestamp) | + | statistics.last_contact_reply_at | Date (UNIX timestamp) | + | statistics.last_admin_reply_at | Date (UNIX timestamp) | + | statistics.last_close_at | Date (UNIX timestamp) | + | statistics.last_closed_by_id | String | + | statistics.count_reopens | Integer | + | statistics.count_assignments | Integer | + | statistics.count_conversation_parts | Integer | + | conversation_rating.requested_at | Date (UNIX timestamp) | + | conversation_rating.replied_at | Date (UNIX timestamp) | + | conversation_rating.score | Integer | + | conversation_rating.remark | String | + | conversation_rating.contact_id | String | + | conversation_rating.admin_d | String | + | ai_agent_participated | Boolean | + | ai_agent.resolution_state | String | + | ai_agent.last_answer_type | String | + | ai_agent.rating | Integer | + | ai_agent.rating_remark | String | + | ai_agent.source_type | String | + | ai_agent.source_title | String | + + ### Accepted Operators + + 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 Shortcut for `OR` queries Values most be in Array | + | NIN | All | Not In Shortcut for `OR !` queries Values must be in Array | + | > | Integer Date (UNIX Timestamp) | Greater (or equal) than | + | < | Integer Date (UNIX Timestamp) | Lower (or equal) 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 + ------- + AsyncHttpResponse[ConversationList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "conversations/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: + _data = typing.cast( + ConversationList, + construct_type( + type_=ConversationList, # 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 reply_conversation( + self, id: str, *, request: ReplyConversationRequestBody, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Conversation]: + """ + You can reply to a conversation with a message from an admin or on behalf of a contact, or with a note for admins. + + Parameters + ---------- + id : str + The Intercom provisioned identifier for the conversation or the string "last" to reply to the last part of the conversation + + request : ReplyConversationRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Conversation] + User last conversation reply + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(id)}/reply", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=ReplyConversationRequestBody, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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 manage_conversation( + self, + id: str, + *, + request: ManageConversationRequestBody, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Conversation]: + """ + For managing conversations you can: + - Close a conversation + - Snooze a conversation to reopen on a future date + - Open a conversation which is `snoozed` or `closed` + - Assign a conversation to an admin and/or team. + + Parameters + ---------- + id : str + The identifier for the conversation as given by Intercom. + + request : ManageConversationRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Conversation] + Assign a conversation + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(id)}/parts", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=ManageConversationRequestBody, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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_contact_to_conversation( + self, + id: str, + *, + admin_id: typing.Optional[str] = OMIT, + customer: typing.Optional[AttachContactToConversationRequestCustomer] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Conversation]: + """ + You can add participants who are contacts to a conversation, on behalf of either another contact or an admin. + + {% admonition type="warning" name="Contacts without an email" %} + If you add a contact via the email parameter and there is no user/lead found on that workspace with he given email, then we will create a new contact with `role` set to `lead`. + {% /admonition %} + + Parameters + ---------- + id : str + The identifier for the conversation as given by Intercom. + + admin_id : typing.Optional[str] + The `id` of the admin who is adding the new participant. + + customer : typing.Optional[AttachContactToConversationRequestCustomer] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Conversation] + Attach a contact to a conversation + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(id)}/customers", + method="POST", + json={ + "admin_id": admin_id, + "customer": convert_and_respect_annotation_metadata( + object_=customer, annotation=AttachContactToConversationRequestCustomer, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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_contact_from_conversation( + self, + conversation_id: str, + contact_id: str, + *, + admin_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Conversation]: + """ + You can add participants who are contacts to a conversation, on behalf of either another contact or an admin. + + {% admonition type="warning" name="Contacts without an email" %} + If you add a contact via the email parameter and there is no user/lead found on that workspace with he given email, then we will create a new contact with `role` set to `lead`. + {% /admonition %} + + Parameters + ---------- + conversation_id : str + The identifier for the conversation as given by Intercom. + + contact_id : str + The identifier for the contact as given by Intercom. + + admin_id : str + The `id` of the admin who is performing the action. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Conversation] + Detach a contact from a group conversation + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/customers/{jsonable_encoder(contact_id)}", + method="DELETE", + json={ + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 == 403: + raise ForbiddenError( + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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 redact_conversation( + self, *, request: RedactConversationRequest, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Conversation]: + """ + You can redact a conversation part or the source message of a conversation (as seen in the source object). + + {% admonition type="info" name="Redacting parts and messages" %} + If you are redacting a conversation part, it must have a `body`. If you are redacting a source message, it must have been created by a contact. We will return a `conversation_part_not_redactable` error if these criteria are not met. + {% /admonition %} + + Parameters + ---------- + request : RedactConversationRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Conversation] + Redact a conversation part + """ + _response = await self._client_wrapper.httpx_client.request( + "conversations/redact", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=RedactConversationRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Conversation, + construct_type( + type_=Conversation, # 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 convert_conversation_to_ticket( + self, + id: int, + *, + ticket_type_id: str, + attributes: typing.Optional[TicketRequestCustomAttributes] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[typing.Optional[Ticket]]: + """ + You can convert a conversation to a ticket. + + Parameters + ---------- + id : int + The id of the conversation to target + + ticket_type_id : str + The ID of the type of ticket you want to convert the conversation to + + attributes : typing.Optional[TicketRequestCustomAttributes] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[Ticket]] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(id)}/convert", + method="POST", + json={ + "ticket_type_id": ticket_type_id, + "attributes": convert_and_respect_annotation_metadata( + object_=attributes, annotation=TicketRequestCustomAttributes, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Ticket], + construct_type( + type_=typing.Optional[Ticket], # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + 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) diff --git a/src/intercom/unstable/conversations/types/__init__.py b/src/intercom/unstable/conversations/types/__init__.py new file mode 100644 index 00000000..fbadd5a4 --- /dev/null +++ b/src/intercom/unstable/conversations/types/__init__.py @@ -0,0 +1,85 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .attach_contact_to_conversation_request_customer import AttachContactToConversationRequestCustomer + from .attach_contact_to_conversation_request_customer_customer import ( + AttachContactToConversationRequestCustomerCustomer, + ) + from .attach_contact_to_conversation_request_customer_intercom_user_id import ( + AttachContactToConversationRequestCustomerIntercomUserId, + ) + from .attach_contact_to_conversation_request_customer_user_id import ( + AttachContactToConversationRequestCustomerUserId, + ) + from .conversation import Conversation + from .conversation_priority import ConversationPriority + from .conversation_state import ConversationState + from .create_conversation_request_from import CreateConversationRequestFrom + from .create_conversation_request_from_type import CreateConversationRequestFromType + from .manage_conversation_request_body import ( + ManageConversationRequestBody, + ManageConversationRequestBody_Assignment, + ManageConversationRequestBody_Close, + ManageConversationRequestBody_Open, + ManageConversationRequestBody_Snoozed, + ) +_dynamic_imports: typing.Dict[str, str] = { + "AttachContactToConversationRequestCustomer": ".attach_contact_to_conversation_request_customer", + "AttachContactToConversationRequestCustomerCustomer": ".attach_contact_to_conversation_request_customer_customer", + "AttachContactToConversationRequestCustomerIntercomUserId": ".attach_contact_to_conversation_request_customer_intercom_user_id", + "AttachContactToConversationRequestCustomerUserId": ".attach_contact_to_conversation_request_customer_user_id", + "Conversation": ".conversation", + "ConversationPriority": ".conversation_priority", + "ConversationState": ".conversation_state", + "CreateConversationRequestFrom": ".create_conversation_request_from", + "CreateConversationRequestFromType": ".create_conversation_request_from_type", + "ManageConversationRequestBody": ".manage_conversation_request_body", + "ManageConversationRequestBody_Assignment": ".manage_conversation_request_body", + "ManageConversationRequestBody_Close": ".manage_conversation_request_body", + "ManageConversationRequestBody_Open": ".manage_conversation_request_body", + "ManageConversationRequestBody_Snoozed": ".manage_conversation_request_body", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "AttachContactToConversationRequestCustomer", + "AttachContactToConversationRequestCustomerCustomer", + "AttachContactToConversationRequestCustomerIntercomUserId", + "AttachContactToConversationRequestCustomerUserId", + "Conversation", + "ConversationPriority", + "ConversationState", + "CreateConversationRequestFrom", + "CreateConversationRequestFromType", + "ManageConversationRequestBody", + "ManageConversationRequestBody_Assignment", + "ManageConversationRequestBody_Close", + "ManageConversationRequestBody_Open", + "ManageConversationRequestBody_Snoozed", +] diff --git a/src/intercom/unstable/conversations/types/attach_contact_to_conversation_request_customer.py b/src/intercom/unstable/conversations/types/attach_contact_to_conversation_request_customer.py new file mode 100644 index 00000000..81152a42 --- /dev/null +++ b/src/intercom/unstable/conversations/types/attach_contact_to_conversation_request_customer.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .attach_contact_to_conversation_request_customer_customer import AttachContactToConversationRequestCustomerCustomer +from .attach_contact_to_conversation_request_customer_intercom_user_id import ( + AttachContactToConversationRequestCustomerIntercomUserId, +) +from .attach_contact_to_conversation_request_customer_user_id import AttachContactToConversationRequestCustomerUserId + +AttachContactToConversationRequestCustomer = typing.Union[ + AttachContactToConversationRequestCustomerIntercomUserId, + AttachContactToConversationRequestCustomerUserId, + AttachContactToConversationRequestCustomerCustomer, +] diff --git a/src/intercom/unstable/conversations/types/attach_contact_to_conversation_request_customer_customer.py b/src/intercom/unstable/conversations/types/attach_contact_to_conversation_request_customer_customer.py new file mode 100644 index 00000000..0d06af39 --- /dev/null +++ b/src/intercom/unstable/conversations/types/attach_contact_to_conversation_request_customer_customer.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...types.customer_request import CustomerRequest + + +class AttachContactToConversationRequestCustomerCustomer(UncheckedBaseModel): + email: str = pydantic.Field() + """ + The email you have defined for the contact who is being added as a participant. + """ + + customer: typing.Optional[CustomerRequest] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/conversations/types/attach_contact_to_conversation_request_customer_intercom_user_id.py b/src/intercom/unstable/conversations/types/attach_contact_to_conversation_request_customer_intercom_user_id.py new file mode 100644 index 00000000..d2152d66 --- /dev/null +++ b/src/intercom/unstable/conversations/types/attach_contact_to_conversation_request_customer_intercom_user_id.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...types.customer_request import CustomerRequest + + +class AttachContactToConversationRequestCustomerIntercomUserId(UncheckedBaseModel): + intercom_user_id: str = pydantic.Field() + """ + The identifier for the contact as given by Intercom. + """ + + customer: typing.Optional[CustomerRequest] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/conversations/types/attach_contact_to_conversation_request_customer_user_id.py b/src/intercom/unstable/conversations/types/attach_contact_to_conversation_request_customer_user_id.py new file mode 100644 index 00000000..65ab29d5 --- /dev/null +++ b/src/intercom/unstable/conversations/types/attach_contact_to_conversation_request_customer_user_id.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...types.customer_request import CustomerRequest + + +class AttachContactToConversationRequestCustomerUserId(UncheckedBaseModel): + user_id: str = pydantic.Field() + """ + The external_id you have defined for the contact who is being added as a participant. + """ + + customer: typing.Optional[CustomerRequest] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/conversations/types/conversation.py b/src/intercom/unstable/conversations/types/conversation.py new file mode 100644 index 00000000..76f27363 --- /dev/null +++ b/src/intercom/unstable/conversations/types/conversation.py @@ -0,0 +1,124 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...ai_agent.types.ai_agent import AiAgent +from ...types.conversation_contacts import ConversationContacts +from ...types.conversation_first_contact_reply import ConversationFirstContactReply +from ...types.conversation_parts import ConversationParts +from ...types.conversation_rating import ConversationRating +from ...types.conversation_source import ConversationSource +from ...types.conversation_statistics import ConversationStatistics +from ...types.conversation_teammates import ConversationTeammates +from ...types.custom_attributes import CustomAttributes +from ...types.linked_object_list import LinkedObjectList +from ...types.sla_applied import SlaApplied +from ...types.tags import Tags +from .conversation_priority import ConversationPriority +from .conversation_state import ConversationState + + +class Conversation(UncheckedBaseModel): + """ + Conversations are how you can communicate with users in Intercom. They are created when a contact replies to an outbound message, or when one admin directly sends a message to a single contact. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + Always conversation. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the conversation. + """ + + title: typing.Optional[str] = pydantic.Field(default=None) + """ + The title given to the conversation. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the conversation was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The last time the conversation was updated. + """ + + waiting_since: typing.Optional[int] = pydantic.Field(default=None) + """ + The last time a Contact responded to an Admin. In other words, the time a customer started waiting for a response. Set to null if last reply is from an Admin. + """ + + snoozed_until: typing.Optional[int] = pydantic.Field(default=None) + """ + If set this is the time in the future when this conversation will be marked as open. i.e. it will be in a snoozed state until this time. i.e. it will be in a snoozed state until this time. + """ + + open: typing.Optional[bool] = pydantic.Field(default=None) + """ + Indicates whether a conversation is open (true) or closed (false). + """ + + state: typing.Optional[ConversationState] = pydantic.Field(default=None) + """ + Can be set to "open", "closed" or "snoozed". + """ + + read: typing.Optional[bool] = pydantic.Field(default=None) + """ + Indicates whether a conversation has been read. + """ + + priority: typing.Optional[ConversationPriority] = pydantic.Field(default=None) + """ + If marked as priority, it will return priority or else not_priority. + """ + + admin_assignee_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of the admin assigned to the conversation. If it's not assigned to an admin it will return null. + """ + + team_assignee_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the team assigned to the conversation. If it's not assigned to a team it will return null. + """ + + company_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the company that the conversation is associated with. The unique identifier for the company which is given by Intercom. + """ + + tags: typing.Optional[Tags] = None + conversation_rating: typing.Optional[ConversationRating] = None + source: typing.Optional[ConversationSource] = None + contacts: typing.Optional[ConversationContacts] = None + teammates: typing.Optional[ConversationTeammates] = None + custom_attributes: typing.Optional[CustomAttributes] = None + first_contact_reply: typing.Optional[ConversationFirstContactReply] = None + sla_applied: typing.Optional[SlaApplied] = None + statistics: typing.Optional[ConversationStatistics] = None + conversation_parts: typing.Optional[ConversationParts] = None + linked_objects: typing.Optional[LinkedObjectList] = None + ai_agent_participated: typing.Optional[bool] = pydantic.Field(default=None) + """ + Indicates whether the AI Agent participated in the conversation. + """ + + ai_agent: typing.Optional[AiAgent] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/conversations/types/conversation_priority.py b/src/intercom/unstable/conversations/types/conversation_priority.py new file mode 100644 index 00000000..c70400ba --- /dev/null +++ b/src/intercom/unstable/conversations/types/conversation_priority.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ConversationPriority = typing.Union[typing.Literal["priority", "not_priority"], typing.Any] diff --git a/src/intercom/unstable/conversations/types/conversation_state.py b/src/intercom/unstable/conversations/types/conversation_state.py new file mode 100644 index 00000000..2353c122 --- /dev/null +++ b/src/intercom/unstable/conversations/types/conversation_state.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ConversationState = typing.Union[typing.Literal["open", "closed", "snoozed"], typing.Any] diff --git a/src/intercom/unstable/conversations/types/create_conversation_request_from.py b/src/intercom/unstable/conversations/types/create_conversation_request_from.py new file mode 100644 index 00000000..edbb07e5 --- /dev/null +++ b/src/intercom/unstable/conversations/types/create_conversation_request_from.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .create_conversation_request_from_type import CreateConversationRequestFromType + + +class CreateConversationRequestFrom(UncheckedBaseModel): + type: CreateConversationRequestFromType = pydantic.Field() + """ + The role associated to the contact - user or lead. + """ + + id: str = pydantic.Field() + """ + The identifier for the contact which is given by Intercom. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/conversations/types/create_conversation_request_from_type.py b/src/intercom/unstable/conversations/types/create_conversation_request_from_type.py new file mode 100644 index 00000000..bd885295 --- /dev/null +++ b/src/intercom/unstable/conversations/types/create_conversation_request_from_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CreateConversationRequestFromType = typing.Union[typing.Literal["lead", "user", "contact"], typing.Any] diff --git a/src/intercom/unstable/conversations/types/manage_conversation_request_body.py b/src/intercom/unstable/conversations/types/manage_conversation_request_body.py new file mode 100644 index 00000000..92a2dcca --- /dev/null +++ b/src/intercom/unstable/conversations/types/manage_conversation_request_body.py @@ -0,0 +1,84 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import pydantic +import typing_extensions +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel, UnionMetadata +from ...types.assign_conversation_request_type import AssignConversationRequestType + + +class ManageConversationRequestBody_Close(UncheckedBaseModel): + message_type: typing.Literal["close"] = "close" + type: typing.Literal["admin"] = "admin" + admin_id: str + body: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ManageConversationRequestBody_Snoozed(UncheckedBaseModel): + message_type: typing.Literal["snoozed"] = "snoozed" + admin_id: str + snoozed_until: int + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ManageConversationRequestBody_Open(UncheckedBaseModel): + message_type: typing.Literal["open"] = "open" + admin_id: str + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class ManageConversationRequestBody_Assignment(UncheckedBaseModel): + message_type: typing.Literal["assignment"] = "assignment" + type: AssignConversationRequestType + admin_id: str + assignee_id: str + body: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +ManageConversationRequestBody = typing_extensions.Annotated[ + typing.Union[ + ManageConversationRequestBody_Close, + ManageConversationRequestBody_Snoozed, + ManageConversationRequestBody_Open, + ManageConversationRequestBody_Assignment, + ], + UnionMetadata(discriminant="message_type"), +] diff --git a/src/intercom/unstable/custom_channel_events/__init__.py b/src/intercom/unstable/custom_channel_events/__init__.py new file mode 100644 index 00000000..5cde0202 --- /dev/null +++ b/src/intercom/unstable/custom_channel_events/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/src/intercom/unstable/custom_channel_events/client.py b/src/intercom/unstable/custom_channel_events/client.py new file mode 100644 index 00000000..7ae618d2 --- /dev/null +++ b/src/intercom/unstable/custom_channel_events/client.py @@ -0,0 +1,573 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..types.custom_channel_attribute import CustomChannelAttribute +from ..types.custom_channel_contact import CustomChannelContact +from ..types.custom_channel_notification_response import CustomChannelNotificationResponse +from .raw_client import AsyncRawCustomChannelEventsClient, RawCustomChannelEventsClient + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class CustomChannelEventsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawCustomChannelEventsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawCustomChannelEventsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawCustomChannelEventsClient + """ + return self._raw_client + + def notify_new_conversation( + self, + *, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomChannelNotificationResponse: + """ + Notifies Intercom that a new conversation was created in your custom channel/platform. This triggers conversation creation and workflow automations within Intercom for your custom channel integration. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomChannelNotificationResponse + Successfully notified Intercom + + Examples + -------- + from intercom import Intercom + from intercom.unstable import CustomChannelContact + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.custom_channel_events.notify_new_conversation( + event_id="evt_12345", + external_conversation_id="conv_67890", + contact=CustomChannelContact( + type="user", + external_id="user_001", + name="Jane Doe", + email="jane.doe@example.com", + ), + ) + """ + _response = self._raw_client.notify_new_conversation( + event_id=event_id, + external_conversation_id=external_conversation_id, + contact=contact, + request_options=request_options, + ) + return _response.data + + def notify_new_message( + self, + *, + body: str, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomChannelNotificationResponse: + """ + Notifies Intercom that a new message was sent in a conversation on your custom channel/platform. This allows Intercom to process the message and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + body : str + The message content sent by the user. + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomChannelNotificationResponse + Successfully notified Intercom + + Examples + -------- + from intercom import Intercom + from intercom.unstable import CustomChannelContact + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.custom_channel_events.notify_new_message( + event_id="evt_54321", + external_conversation_id="conv_98765", + contact=CustomChannelContact( + type="user", + external_id="user_002", + name="John Smith", + email="john.smith@example.com", + ), + body="Hello, I need help with my order.", + ) + """ + _response = self._raw_client.notify_new_message( + body=body, + event_id=event_id, + external_conversation_id=external_conversation_id, + contact=contact, + request_options=request_options, + ) + return _response.data + + def notify_quick_reply_selected( + self, + *, + quick_reply_option_id: str, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomChannelNotificationResponse: + """ + Notifies Intercom that a user selected a quick reply option in your custom channel/platform. This allows Intercom to process the response and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + quick_reply_option_id : str + Id of the selected quick reply option. + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomChannelNotificationResponse + Successfully notified Intercom + + Examples + -------- + from intercom import Intercom + from intercom.unstable import CustomChannelContact + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.custom_channel_events.notify_quick_reply_selected( + event_id="evt_67890", + external_conversation_id="conv_13579", + contact=CustomChannelContact( + type="user", + external_id="user_003", + name="Alice Example", + email="alice@example.com", + ), + quick_reply_option_id="1234", + ) + """ + _response = self._raw_client.notify_quick_reply_selected( + quick_reply_option_id=quick_reply_option_id, + event_id=event_id, + external_conversation_id=external_conversation_id, + contact=contact, + request_options=request_options, + ) + return _response.data + + def notify_attribute_collected( + self, + *, + attribute: CustomChannelAttribute, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomChannelNotificationResponse: + """ + Notifies Intercom that a user provided a response to an attribute collector in your custom channel/platform. This allows Intercom to process the attribute and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + attribute : CustomChannelAttribute + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomChannelNotificationResponse + Successfully notified Intercom + + Examples + -------- + from intercom import Intercom + from intercom.unstable import CustomChannelAttribute, CustomChannelContact + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.custom_channel_events.notify_attribute_collected( + event_id="evt_24680", + external_conversation_id="conv_11223", + contact=CustomChannelContact( + type="user", + external_id="user_004", + name="Bob Example", + email="bob@example.com", + ), + attribute=CustomChannelAttribute( + id="shipping_address", + value="123 Main St, Springfield", + ), + ) + """ + _response = self._raw_client.notify_attribute_collected( + attribute=attribute, + event_id=event_id, + external_conversation_id=external_conversation_id, + contact=contact, + request_options=request_options, + ) + return _response.data + + +class AsyncCustomChannelEventsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawCustomChannelEventsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawCustomChannelEventsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawCustomChannelEventsClient + """ + return self._raw_client + + async def notify_new_conversation( + self, + *, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomChannelNotificationResponse: + """ + Notifies Intercom that a new conversation was created in your custom channel/platform. This triggers conversation creation and workflow automations within Intercom for your custom channel integration. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomChannelNotificationResponse + Successfully notified Intercom + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.unstable import CustomChannelContact + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.custom_channel_events.notify_new_conversation( + event_id="evt_12345", + external_conversation_id="conv_67890", + contact=CustomChannelContact( + type="user", + external_id="user_001", + name="Jane Doe", + email="jane.doe@example.com", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.notify_new_conversation( + event_id=event_id, + external_conversation_id=external_conversation_id, + contact=contact, + request_options=request_options, + ) + return _response.data + + async def notify_new_message( + self, + *, + body: str, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomChannelNotificationResponse: + """ + Notifies Intercom that a new message was sent in a conversation on your custom channel/platform. This allows Intercom to process the message and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + body : str + The message content sent by the user. + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomChannelNotificationResponse + Successfully notified Intercom + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.unstable import CustomChannelContact + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.custom_channel_events.notify_new_message( + event_id="evt_54321", + external_conversation_id="conv_98765", + contact=CustomChannelContact( + type="user", + external_id="user_002", + name="John Smith", + email="john.smith@example.com", + ), + body="Hello, I need help with my order.", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.notify_new_message( + body=body, + event_id=event_id, + external_conversation_id=external_conversation_id, + contact=contact, + request_options=request_options, + ) + return _response.data + + async def notify_quick_reply_selected( + self, + *, + quick_reply_option_id: str, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomChannelNotificationResponse: + """ + Notifies Intercom that a user selected a quick reply option in your custom channel/platform. This allows Intercom to process the response and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + quick_reply_option_id : str + Id of the selected quick reply option. + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomChannelNotificationResponse + Successfully notified Intercom + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.unstable import CustomChannelContact + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.custom_channel_events.notify_quick_reply_selected( + event_id="evt_67890", + external_conversation_id="conv_13579", + contact=CustomChannelContact( + type="user", + external_id="user_003", + name="Alice Example", + email="alice@example.com", + ), + quick_reply_option_id="1234", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.notify_quick_reply_selected( + quick_reply_option_id=quick_reply_option_id, + event_id=event_id, + external_conversation_id=external_conversation_id, + contact=contact, + request_options=request_options, + ) + return _response.data + + async def notify_attribute_collected( + self, + *, + attribute: CustomChannelAttribute, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomChannelNotificationResponse: + """ + Notifies Intercom that a user provided a response to an attribute collector in your custom channel/platform. This allows Intercom to process the attribute and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + attribute : CustomChannelAttribute + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomChannelNotificationResponse + Successfully notified Intercom + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.unstable import CustomChannelAttribute, CustomChannelContact + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.custom_channel_events.notify_attribute_collected( + event_id="evt_24680", + external_conversation_id="conv_11223", + contact=CustomChannelContact( + type="user", + external_id="user_004", + name="Bob Example", + email="bob@example.com", + ), + attribute=CustomChannelAttribute( + id="shipping_address", + value="123 Main St, Springfield", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.notify_attribute_collected( + attribute=attribute, + event_id=event_id, + external_conversation_id=external_conversation_id, + contact=contact, + request_options=request_options, + ) + return _response.data diff --git a/src/intercom/unstable/custom_channel_events/raw_client.py b/src/intercom/unstable/custom_channel_events/raw_client.py new file mode 100644 index 00000000..21622446 --- /dev/null +++ b/src/intercom/unstable/custom_channel_events/raw_client.py @@ -0,0 +1,904 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.http_response import AsyncHttpResponse, HttpResponse +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.bad_request_error import BadRequestError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..errors.unprocessable_entity_error import UnprocessableEntityError +from ..types.custom_channel_attribute import CustomChannelAttribute +from ..types.custom_channel_contact import CustomChannelContact +from ..types.custom_channel_notification_response import CustomChannelNotificationResponse +from ..types.error import Error + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawCustomChannelEventsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def notify_new_conversation( + self, + *, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[CustomChannelNotificationResponse]: + """ + Notifies Intercom that a new conversation was created in your custom channel/platform. This triggers conversation creation and workflow automations within Intercom for your custom channel integration. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CustomChannelNotificationResponse] + Successfully notified Intercom + """ + _response = self._client_wrapper.httpx_client.request( + "custom_channel_events/notify_new_conversation", + method="POST", + json={ + "event_id": event_id, + "external_conversation_id": external_conversation_id, + "contact": convert_and_respect_annotation_metadata( + object_=contact, annotation=CustomChannelContact, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomChannelNotificationResponse, + construct_type( + type_=CustomChannelNotificationResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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 notify_new_message( + self, + *, + body: str, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[CustomChannelNotificationResponse]: + """ + Notifies Intercom that a new message was sent in a conversation on your custom channel/platform. This allows Intercom to process the message and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + body : str + The message content sent by the user. + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CustomChannelNotificationResponse] + Successfully notified Intercom + """ + _response = self._client_wrapper.httpx_client.request( + "custom_channel_events/notify_new_message", + method="POST", + json={ + "body": body, + "event_id": event_id, + "external_conversation_id": external_conversation_id, + "contact": convert_and_respect_annotation_metadata( + object_=contact, annotation=CustomChannelContact, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomChannelNotificationResponse, + construct_type( + type_=CustomChannelNotificationResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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 notify_quick_reply_selected( + self, + *, + quick_reply_option_id: str, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[CustomChannelNotificationResponse]: + """ + Notifies Intercom that a user selected a quick reply option in your custom channel/platform. This allows Intercom to process the response and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + quick_reply_option_id : str + Id of the selected quick reply option. + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CustomChannelNotificationResponse] + Successfully notified Intercom + """ + _response = self._client_wrapper.httpx_client.request( + "custom_channel_events/notify_quick_reply_selected", + method="POST", + json={ + "quick_reply_option_id": quick_reply_option_id, + "event_id": event_id, + "external_conversation_id": external_conversation_id, + "contact": convert_and_respect_annotation_metadata( + object_=contact, annotation=CustomChannelContact, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomChannelNotificationResponse, + construct_type( + type_=CustomChannelNotificationResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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 notify_attribute_collected( + self, + *, + attribute: CustomChannelAttribute, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[CustomChannelNotificationResponse]: + """ + Notifies Intercom that a user provided a response to an attribute collector in your custom channel/platform. This allows Intercom to process the attribute and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + attribute : CustomChannelAttribute + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CustomChannelNotificationResponse] + Successfully notified Intercom + """ + _response = self._client_wrapper.httpx_client.request( + "custom_channel_events/notify_attribute_collected", + method="POST", + json={ + "attribute": convert_and_respect_annotation_metadata( + object_=attribute, annotation=CustomChannelAttribute, direction="write" + ), + "event_id": event_id, + "external_conversation_id": external_conversation_id, + "contact": convert_and_respect_annotation_metadata( + object_=contact, annotation=CustomChannelContact, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomChannelNotificationResponse, + construct_type( + type_=CustomChannelNotificationResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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) + + +class AsyncRawCustomChannelEventsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def notify_new_conversation( + self, + *, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[CustomChannelNotificationResponse]: + """ + Notifies Intercom that a new conversation was created in your custom channel/platform. This triggers conversation creation and workflow automations within Intercom for your custom channel integration. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CustomChannelNotificationResponse] + Successfully notified Intercom + """ + _response = await self._client_wrapper.httpx_client.request( + "custom_channel_events/notify_new_conversation", + method="POST", + json={ + "event_id": event_id, + "external_conversation_id": external_conversation_id, + "contact": convert_and_respect_annotation_metadata( + object_=contact, annotation=CustomChannelContact, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomChannelNotificationResponse, + construct_type( + type_=CustomChannelNotificationResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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 notify_new_message( + self, + *, + body: str, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[CustomChannelNotificationResponse]: + """ + Notifies Intercom that a new message was sent in a conversation on your custom channel/platform. This allows Intercom to process the message and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + body : str + The message content sent by the user. + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CustomChannelNotificationResponse] + Successfully notified Intercom + """ + _response = await self._client_wrapper.httpx_client.request( + "custom_channel_events/notify_new_message", + method="POST", + json={ + "body": body, + "event_id": event_id, + "external_conversation_id": external_conversation_id, + "contact": convert_and_respect_annotation_metadata( + object_=contact, annotation=CustomChannelContact, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomChannelNotificationResponse, + construct_type( + type_=CustomChannelNotificationResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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 notify_quick_reply_selected( + self, + *, + quick_reply_option_id: str, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[CustomChannelNotificationResponse]: + """ + Notifies Intercom that a user selected a quick reply option in your custom channel/platform. This allows Intercom to process the response and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + quick_reply_option_id : str + Id of the selected quick reply option. + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CustomChannelNotificationResponse] + Successfully notified Intercom + """ + _response = await self._client_wrapper.httpx_client.request( + "custom_channel_events/notify_quick_reply_selected", + method="POST", + json={ + "quick_reply_option_id": quick_reply_option_id, + "event_id": event_id, + "external_conversation_id": external_conversation_id, + "contact": convert_and_respect_annotation_metadata( + object_=contact, annotation=CustomChannelContact, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomChannelNotificationResponse, + construct_type( + type_=CustomChannelNotificationResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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 notify_attribute_collected( + self, + *, + attribute: CustomChannelAttribute, + event_id: str, + external_conversation_id: str, + contact: CustomChannelContact, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[CustomChannelNotificationResponse]: + """ + Notifies Intercom that a user provided a response to an attribute collector in your custom channel/platform. This allows Intercom to process the attribute and trigger any relevant workflow automations. + > **Note:** This endpoint is currently under managed availability. Please reach out to your accounts team to discuss access and tailored, hands-on support. + + Parameters + ---------- + attribute : CustomChannelAttribute + + event_id : str + Unique identifier for the event. + + external_conversation_id : str + Identifier for the conversation in your application. + + contact : CustomChannelContact + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CustomChannelNotificationResponse] + Successfully notified Intercom + """ + _response = await self._client_wrapper.httpx_client.request( + "custom_channel_events/notify_attribute_collected", + method="POST", + json={ + "attribute": convert_and_respect_annotation_metadata( + object_=attribute, annotation=CustomChannelAttribute, direction="write" + ), + "event_id": event_id, + "external_conversation_id": external_conversation_id, + "contact": convert_and_respect_annotation_metadata( + object_=contact, annotation=CustomChannelContact, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomChannelNotificationResponse, + construct_type( + type_=CustomChannelNotificationResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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) diff --git a/src/intercom/unstable/custom_object_instances/__init__.py b/src/intercom/unstable/custom_object_instances/__init__.py new file mode 100644 index 00000000..f9a5fd47 --- /dev/null +++ b/src/intercom/unstable/custom_object_instances/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import CustomObjectInstance +_dynamic_imports: typing.Dict[str, str] = {"CustomObjectInstance": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["CustomObjectInstance"] diff --git a/src/intercom/unstable/custom_object_instances/client.py b/src/intercom/unstable/custom_object_instances/client.py new file mode 100644 index 00000000..6f25938c --- /dev/null +++ b/src/intercom/unstable/custom_object_instances/client.py @@ -0,0 +1,540 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..types.custom_object_instance_deleted import CustomObjectInstanceDeleted +from .raw_client import AsyncRawCustomObjectInstancesClient, RawCustomObjectInstancesClient +from .types.custom_object_instance import CustomObjectInstance + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class CustomObjectInstancesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawCustomObjectInstancesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawCustomObjectInstancesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawCustomObjectInstancesClient + """ + return self._raw_client + + def get_custom_object_instances_by_external_id( + self, + custom_object_type_identifier: str, + *, + external_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[CustomObjectInstance]: + """ + Fetch a Custom Object Instance by external_id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[CustomObjectInstance] + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.custom_object_instances.get_custom_object_instances_by_external_id( + custom_object_type_identifier="Order", + external_id="external_id", + ) + """ + _response = self._raw_client.get_custom_object_instances_by_external_id( + custom_object_type_identifier, external_id=external_id, request_options=request_options + ) + return _response.data + + def create_custom_object_instances( + self, + custom_object_type_identifier: str, + *, + external_id: typing.Optional[str] = OMIT, + external_created_at: typing.Optional[int] = OMIT, + external_updated_at: typing.Optional[int] = OMIT, + custom_attributes: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[CustomObjectInstance]: + """ + Create or update a custom object instance + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : typing.Optional[str] + A unique identifier for the Custom Object instance in the external system it originated from. + + external_created_at : typing.Optional[int] + The time when the Custom Object instance was created in the external system it originated from. + + external_updated_at : typing.Optional[int] + The time when the Custom Object instance was last updated in the external system it originated from. + + custom_attributes : typing.Optional[typing.Dict[str, typing.Optional[str]]] + The custom attributes which are set for the Custom Object instance. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[CustomObjectInstance] + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.custom_object_instances.create_custom_object_instances( + custom_object_type_identifier="Order", + external_id="123", + external_created_at=1392036272, + external_updated_at=1392036272, + custom_attributes={ + "order_number": "ORDER-12345", + "total_amount": "custom_attributes", + }, + ) + """ + _response = self._raw_client.create_custom_object_instances( + custom_object_type_identifier, + external_id=external_id, + external_created_at=external_created_at, + external_updated_at=external_updated_at, + custom_attributes=custom_attributes, + request_options=request_options, + ) + return _response.data + + def delete_custom_object_instances_by_id( + self, + custom_object_type_identifier: str, + *, + external_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomObjectInstanceDeleted: + """ + Delete a single Custom Object instance by external_id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomObjectInstanceDeleted + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.custom_object_instances.delete_custom_object_instances_by_id( + custom_object_type_identifier="Order", + external_id="external_id", + ) + """ + _response = self._raw_client.delete_custom_object_instances_by_id( + custom_object_type_identifier, external_id=external_id, request_options=request_options + ) + return _response.data + + def get_custom_object_instances_by_id( + self, custom_object_type_identifier: str, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[CustomObjectInstance]: + """ + Fetch a Custom Object Instance by id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + id : str + The id or external_id of the custom object instance + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[CustomObjectInstance] + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.custom_object_instances.get_custom_object_instances_by_id( + custom_object_type_identifier="Order", + id="id", + ) + """ + _response = self._raw_client.get_custom_object_instances_by_id( + custom_object_type_identifier, id, request_options=request_options + ) + return _response.data + + def delete_custom_object_instances_by_external_id( + self, custom_object_type_identifier: str, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> CustomObjectInstanceDeleted: + """ + Delete a single Custom Object instance using the Intercom defined id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + id : str + The Intercom defined id of the custom object instance + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomObjectInstanceDeleted + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.custom_object_instances.delete_custom_object_instances_by_external_id( + custom_object_type_identifier="Order", + id="id", + ) + """ + _response = self._raw_client.delete_custom_object_instances_by_external_id( + custom_object_type_identifier, id, request_options=request_options + ) + return _response.data + + +class AsyncCustomObjectInstancesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawCustomObjectInstancesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawCustomObjectInstancesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawCustomObjectInstancesClient + """ + return self._raw_client + + async def get_custom_object_instances_by_external_id( + self, + custom_object_type_identifier: str, + *, + external_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[CustomObjectInstance]: + """ + Fetch a Custom Object Instance by external_id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[CustomObjectInstance] + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.custom_object_instances.get_custom_object_instances_by_external_id( + custom_object_type_identifier="Order", + external_id="external_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_custom_object_instances_by_external_id( + custom_object_type_identifier, external_id=external_id, request_options=request_options + ) + return _response.data + + async def create_custom_object_instances( + self, + custom_object_type_identifier: str, + *, + external_id: typing.Optional[str] = OMIT, + external_created_at: typing.Optional[int] = OMIT, + external_updated_at: typing.Optional[int] = OMIT, + custom_attributes: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[CustomObjectInstance]: + """ + Create or update a custom object instance + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : typing.Optional[str] + A unique identifier for the Custom Object instance in the external system it originated from. + + external_created_at : typing.Optional[int] + The time when the Custom Object instance was created in the external system it originated from. + + external_updated_at : typing.Optional[int] + The time when the Custom Object instance was last updated in the external system it originated from. + + custom_attributes : typing.Optional[typing.Dict[str, typing.Optional[str]]] + The custom attributes which are set for the Custom Object instance. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[CustomObjectInstance] + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.custom_object_instances.create_custom_object_instances( + custom_object_type_identifier="Order", + external_id="123", + external_created_at=1392036272, + external_updated_at=1392036272, + custom_attributes={ + "order_number": "ORDER-12345", + "total_amount": "custom_attributes", + }, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_custom_object_instances( + custom_object_type_identifier, + external_id=external_id, + external_created_at=external_created_at, + external_updated_at=external_updated_at, + custom_attributes=custom_attributes, + request_options=request_options, + ) + return _response.data + + async def delete_custom_object_instances_by_id( + self, + custom_object_type_identifier: str, + *, + external_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> CustomObjectInstanceDeleted: + """ + Delete a single Custom Object instance by external_id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomObjectInstanceDeleted + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.custom_object_instances.delete_custom_object_instances_by_id( + custom_object_type_identifier="Order", + external_id="external_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_custom_object_instances_by_id( + custom_object_type_identifier, external_id=external_id, request_options=request_options + ) + return _response.data + + async def get_custom_object_instances_by_id( + self, custom_object_type_identifier: str, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[CustomObjectInstance]: + """ + Fetch a Custom Object Instance by id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + id : str + The id or external_id of the custom object instance + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[CustomObjectInstance] + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.custom_object_instances.get_custom_object_instances_by_id( + custom_object_type_identifier="Order", + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_custom_object_instances_by_id( + custom_object_type_identifier, id, request_options=request_options + ) + return _response.data + + async def delete_custom_object_instances_by_external_id( + self, custom_object_type_identifier: str, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> CustomObjectInstanceDeleted: + """ + Delete a single Custom Object instance using the Intercom defined id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + id : str + The Intercom defined id of the custom object instance + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CustomObjectInstanceDeleted + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.custom_object_instances.delete_custom_object_instances_by_external_id( + custom_object_type_identifier="Order", + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_custom_object_instances_by_external_id( + custom_object_type_identifier, id, request_options=request_options + ) + return _response.data diff --git a/src/intercom/unstable/custom_object_instances/raw_client.py b/src/intercom/unstable/custom_object_instances/raw_client.py new file mode 100644 index 00000000..c7ff2ac6 --- /dev/null +++ b/src/intercom/unstable/custom_object_instances/raw_client.py @@ -0,0 +1,757 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.custom_object_instance_deleted import CustomObjectInstanceDeleted +from ..types.error import Error +from .types.custom_object_instance import CustomObjectInstance + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawCustomObjectInstancesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def get_custom_object_instances_by_external_id( + self, + custom_object_type_identifier: str, + *, + external_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[typing.Optional[CustomObjectInstance]]: + """ + Fetch a Custom Object Instance by external_id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[CustomObjectInstance]] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"custom_object_instances/{jsonable_encoder(custom_object_type_identifier)}", + method="GET", + params={ + "external_id": external_id, + }, + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[CustomObjectInstance], + construct_type( + type_=typing.Optional[CustomObjectInstance], # 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 create_custom_object_instances( + self, + custom_object_type_identifier: str, + *, + external_id: typing.Optional[str] = OMIT, + external_created_at: typing.Optional[int] = OMIT, + external_updated_at: typing.Optional[int] = OMIT, + custom_attributes: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[typing.Optional[CustomObjectInstance]]: + """ + Create or update a custom object instance + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : typing.Optional[str] + A unique identifier for the Custom Object instance in the external system it originated from. + + external_created_at : typing.Optional[int] + The time when the Custom Object instance was created in the external system it originated from. + + external_updated_at : typing.Optional[int] + The time when the Custom Object instance was last updated in the external system it originated from. + + custom_attributes : typing.Optional[typing.Dict[str, typing.Optional[str]]] + The custom attributes which are set for the Custom Object instance. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[CustomObjectInstance]] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"custom_object_instances/{jsonable_encoder(custom_object_type_identifier)}", + method="POST", + json={ + "external_id": external_id, + "external_created_at": external_created_at, + "external_updated_at": external_updated_at, + "custom_attributes": custom_attributes, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[CustomObjectInstance], + construct_type( + type_=typing.Optional[CustomObjectInstance], # 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 delete_custom_object_instances_by_id( + self, + custom_object_type_identifier: str, + *, + external_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[CustomObjectInstanceDeleted]: + """ + Delete a single Custom Object instance by external_id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CustomObjectInstanceDeleted] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"custom_object_instances/{jsonable_encoder(custom_object_type_identifier)}", + method="DELETE", + params={ + "external_id": external_id, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomObjectInstanceDeleted, + construct_type( + type_=CustomObjectInstanceDeleted, # 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 get_custom_object_instances_by_id( + self, custom_object_type_identifier: str, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[typing.Optional[CustomObjectInstance]]: + """ + Fetch a Custom Object Instance by id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + id : str + The id or external_id of the custom object instance + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[CustomObjectInstance]] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"custom_object_instances/{jsonable_encoder(custom_object_type_identifier)}/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[CustomObjectInstance], + construct_type( + type_=typing.Optional[CustomObjectInstance], # 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 delete_custom_object_instances_by_external_id( + self, custom_object_type_identifier: str, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[CustomObjectInstanceDeleted]: + """ + Delete a single Custom Object instance using the Intercom defined id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + id : str + The Intercom defined id of the custom object instance + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CustomObjectInstanceDeleted] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"custom_object_instances/{jsonable_encoder(custom_object_type_identifier)}/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomObjectInstanceDeleted, + construct_type( + type_=CustomObjectInstanceDeleted, # 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) + + +class AsyncRawCustomObjectInstancesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def get_custom_object_instances_by_external_id( + self, + custom_object_type_identifier: str, + *, + external_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[typing.Optional[CustomObjectInstance]]: + """ + Fetch a Custom Object Instance by external_id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[CustomObjectInstance]] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"custom_object_instances/{jsonable_encoder(custom_object_type_identifier)}", + method="GET", + params={ + "external_id": external_id, + }, + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[CustomObjectInstance], + construct_type( + type_=typing.Optional[CustomObjectInstance], # 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 create_custom_object_instances( + self, + custom_object_type_identifier: str, + *, + external_id: typing.Optional[str] = OMIT, + external_created_at: typing.Optional[int] = OMIT, + external_updated_at: typing.Optional[int] = OMIT, + custom_attributes: typing.Optional[typing.Dict[str, typing.Optional[str]]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[typing.Optional[CustomObjectInstance]]: + """ + Create or update a custom object instance + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : typing.Optional[str] + A unique identifier for the Custom Object instance in the external system it originated from. + + external_created_at : typing.Optional[int] + The time when the Custom Object instance was created in the external system it originated from. + + external_updated_at : typing.Optional[int] + The time when the Custom Object instance was last updated in the external system it originated from. + + custom_attributes : typing.Optional[typing.Dict[str, typing.Optional[str]]] + The custom attributes which are set for the Custom Object instance. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[CustomObjectInstance]] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"custom_object_instances/{jsonable_encoder(custom_object_type_identifier)}", + method="POST", + json={ + "external_id": external_id, + "external_created_at": external_created_at, + "external_updated_at": external_updated_at, + "custom_attributes": custom_attributes, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[CustomObjectInstance], + construct_type( + type_=typing.Optional[CustomObjectInstance], # 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 delete_custom_object_instances_by_id( + self, + custom_object_type_identifier: str, + *, + external_id: str, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[CustomObjectInstanceDeleted]: + """ + Delete a single Custom Object instance by external_id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + external_id : str + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CustomObjectInstanceDeleted] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"custom_object_instances/{jsonable_encoder(custom_object_type_identifier)}", + method="DELETE", + params={ + "external_id": external_id, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomObjectInstanceDeleted, + construct_type( + type_=CustomObjectInstanceDeleted, # 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 get_custom_object_instances_by_id( + self, custom_object_type_identifier: str, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[typing.Optional[CustomObjectInstance]]: + """ + Fetch a Custom Object Instance by id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + id : str + The id or external_id of the custom object instance + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[CustomObjectInstance]] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"custom_object_instances/{jsonable_encoder(custom_object_type_identifier)}/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[CustomObjectInstance], + construct_type( + type_=typing.Optional[CustomObjectInstance], # 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 delete_custom_object_instances_by_external_id( + self, custom_object_type_identifier: str, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[CustomObjectInstanceDeleted]: + """ + Delete a single Custom Object instance using the Intercom defined id. + + Parameters + ---------- + custom_object_type_identifier : str + The unique identifier of the custom object type that defines the structure of the custom object instance. + + id : str + The Intercom defined id of the custom object instance + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CustomObjectInstanceDeleted] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"custom_object_instances/{jsonable_encoder(custom_object_type_identifier)}/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CustomObjectInstanceDeleted, + construct_type( + type_=CustomObjectInstanceDeleted, # 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) diff --git a/src/intercom/unstable/custom_object_instances/types/__init__.py b/src/intercom/unstable/custom_object_instances/types/__init__.py new file mode 100644 index 00000000..bba7d61b --- /dev/null +++ b/src/intercom/unstable/custom_object_instances/types/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .custom_object_instance import CustomObjectInstance +_dynamic_imports: typing.Dict[str, str] = {"CustomObjectInstance": ".custom_object_instance"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["CustomObjectInstance"] diff --git a/src/intercom/unstable/custom_object_instances/types/custom_object_instance.py b/src/intercom/unstable/custom_object_instances/types/custom_object_instance.py new file mode 100644 index 00000000..c5847988 --- /dev/null +++ b/src/intercom/unstable/custom_object_instances/types/custom_object_instance.py @@ -0,0 +1,62 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class CustomObjectInstance(UncheckedBaseModel): + """ + A Custom Object Instance represents an instance of a custom object type. This allows you to create and set custom attributes to store data about your customers that is not already captured by Intercom. The parent object includes recommended default attributes and you can add your own custom attributes. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom defined id representing the custom object instance. + """ + + external_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id you have defined for the custom object instance. + """ + + external_created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the Custom Object instance was created in the external system it originated from. + """ + + external_updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the Custom Object instance was last updated in the external system it originated from. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the attribute was created as a UTC Unix timestamp + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the attribute was last updated as a UTC Unix timestamp + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + The identifier of the custom object type that defines the structure of the custom object instance. + """ + + custom_attributes: typing.Optional[typing.Dict[str, str]] = pydantic.Field(default=None) + """ + The custom attributes you have set on the custom object instance. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/data_attributes/__init__.py b/src/intercom/unstable/data_attributes/__init__.py new file mode 100644 index 00000000..95af83c9 --- /dev/null +++ b/src/intercom/unstable/data_attributes/__init__.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import DataAttribute, DataAttributeDataType, DataAttributeModel, LisDataAttributesRequestModel +_dynamic_imports: typing.Dict[str, str] = { + "DataAttribute": ".types", + "DataAttributeDataType": ".types", + "DataAttributeModel": ".types", + "LisDataAttributesRequestModel": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["DataAttribute", "DataAttributeDataType", "DataAttributeModel", "LisDataAttributesRequestModel"] diff --git a/src/intercom/unstable/data_attributes/client.py b/src/intercom/unstable/data_attributes/client.py new file mode 100644 index 00000000..62488493 --- /dev/null +++ b/src/intercom/unstable/data_attributes/client.py @@ -0,0 +1,301 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..types.data_attribute_list import DataAttributeList +from .raw_client import AsyncRawDataAttributesClient, RawDataAttributesClient +from .types.data_attribute import DataAttribute +from .types.lis_data_attributes_request_model import LisDataAttributesRequestModel + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class DataAttributesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawDataAttributesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawDataAttributesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawDataAttributesClient + """ + return self._raw_client + + def lis_data_attributes( + self, + *, + model: typing.Optional[LisDataAttributesRequestModel] = None, + include_archived: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> DataAttributeList: + """ + You can fetch a list of all data attributes belonging to a workspace for contacts, companies or conversations. + + Parameters + ---------- + model : typing.Optional[LisDataAttributesRequestModel] + Specify the data attribute model to return. + + include_archived : typing.Optional[bool] + Include archived attributes in the list. By default we return only non archived data attributes. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataAttributeList + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.data_attributes.lis_data_attributes( + model="contact", + include_archived=True, + ) + """ + _response = self._raw_client.lis_data_attributes( + model=model, include_archived=include_archived, request_options=request_options + ) + return _response.data + + def create_data_attribute( + self, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> DataAttribute: + """ + You can create a data attributes for a `contact` or a `company`. + + Parameters + ---------- + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataAttribute + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.data_attributes.create_data_attribute( + request={"key": "value"}, + ) + """ + _response = self._raw_client.create_data_attribute(request=request, request_options=request_options) + return _response.data + + def update_data_attribute( + self, id: int, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> DataAttribute: + """ + + You can update a data attribute. + + > 🚧 Updating the data type is not possible + > + > It is currently a dangerous action to execute changing a data attribute's type via the API. You will need to update the type via the UI instead. + + Parameters + ---------- + id : int + The data attribute id + + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataAttribute + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.data_attributes.update_data_attribute( + id=1, + request={"key": "value"}, + ) + """ + _response = self._raw_client.update_data_attribute(id, request=request, request_options=request_options) + return _response.data + + +class AsyncDataAttributesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawDataAttributesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawDataAttributesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawDataAttributesClient + """ + return self._raw_client + + async def lis_data_attributes( + self, + *, + model: typing.Optional[LisDataAttributesRequestModel] = None, + include_archived: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> DataAttributeList: + """ + You can fetch a list of all data attributes belonging to a workspace for contacts, companies or conversations. + + Parameters + ---------- + model : typing.Optional[LisDataAttributesRequestModel] + Specify the data attribute model to return. + + include_archived : typing.Optional[bool] + Include archived attributes in the list. By default we return only non archived data attributes. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataAttributeList + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.data_attributes.lis_data_attributes( + model="contact", + include_archived=True, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.lis_data_attributes( + model=model, include_archived=include_archived, request_options=request_options + ) + return _response.data + + async def create_data_attribute( + self, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> DataAttribute: + """ + You can create a data attributes for a `contact` or a `company`. + + Parameters + ---------- + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataAttribute + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.data_attributes.create_data_attribute( + request={"key": "value"}, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_data_attribute(request=request, request_options=request_options) + return _response.data + + async def update_data_attribute( + self, id: int, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> DataAttribute: + """ + + You can update a data attribute. + + > 🚧 Updating the data type is not possible + > + > It is currently a dangerous action to execute changing a data attribute's type via the API. You will need to update the type via the UI instead. + + Parameters + ---------- + id : int + The data attribute id + + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataAttribute + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.data_attributes.update_data_attribute( + id=1, + request={"key": "value"}, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update_data_attribute(id, request=request, request_options=request_options) + return _response.data diff --git a/src/intercom/unstable/data_attributes/raw_client.py b/src/intercom/unstable/data_attributes/raw_client.py new file mode 100644 index 00000000..8cdaff36 --- /dev/null +++ b/src/intercom/unstable/data_attributes/raw_client.py @@ -0,0 +1,474 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.bad_request_error import BadRequestError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..errors.unprocessable_entity_error import UnprocessableEntityError +from ..types.data_attribute_list import DataAttributeList +from ..types.error import Error +from .types.data_attribute import DataAttribute +from .types.lis_data_attributes_request_model import LisDataAttributesRequestModel + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawDataAttributesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def lis_data_attributes( + self, + *, + model: typing.Optional[LisDataAttributesRequestModel] = None, + include_archived: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DataAttributeList]: + """ + You can fetch a list of all data attributes belonging to a workspace for contacts, companies or conversations. + + Parameters + ---------- + model : typing.Optional[LisDataAttributesRequestModel] + Specify the data attribute model to return. + + include_archived : typing.Optional[bool] + Include archived attributes in the list. By default we return only non archived data attributes. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DataAttributeList] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "data_attributes", + method="GET", + params={ + "model": model, + "include_archived": include_archived, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataAttributeList, + construct_type( + type_=DataAttributeList, # 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 create_data_attribute( + self, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DataAttribute]: + """ + You can create a data attributes for a `contact` or a `company`. + + Parameters + ---------- + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DataAttribute] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + "data_attributes", + method="POST", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataAttribute, + construct_type( + type_=DataAttribute, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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_data_attribute( + self, id: int, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DataAttribute]: + """ + + You can update a data attribute. + + > 🚧 Updating the data type is not possible + > + > It is currently a dangerous action to execute changing a data attribute's type via the API. You will need to update the type via the UI instead. + + Parameters + ---------- + id : int + The data attribute id + + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DataAttribute] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + f"data_attributes/{jsonable_encoder(id)}", + method="PUT", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataAttribute, + construct_type( + type_=DataAttribute, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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) + + +class AsyncRawDataAttributesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def lis_data_attributes( + self, + *, + model: typing.Optional[LisDataAttributesRequestModel] = None, + include_archived: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DataAttributeList]: + """ + You can fetch a list of all data attributes belonging to a workspace for contacts, companies or conversations. + + Parameters + ---------- + model : typing.Optional[LisDataAttributesRequestModel] + Specify the data attribute model to return. + + include_archived : typing.Optional[bool] + Include archived attributes in the list. By default we return only non archived data attributes. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DataAttributeList] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "data_attributes", + method="GET", + params={ + "model": model, + "include_archived": include_archived, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataAttributeList, + construct_type( + type_=DataAttributeList, # 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 create_data_attribute( + self, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DataAttribute]: + """ + You can create a data attributes for a `contact` or a `company`. + + Parameters + ---------- + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DataAttribute] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + "data_attributes", + method="POST", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataAttribute, + construct_type( + type_=DataAttribute, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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_data_attribute( + self, id: int, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DataAttribute]: + """ + + You can update a data attribute. + + > 🚧 Updating the data type is not possible + > + > It is currently a dangerous action to execute changing a data attribute's type via the API. You will need to update the type via the UI instead. + + Parameters + ---------- + id : int + The data attribute id + + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DataAttribute] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"data_attributes/{jsonable_encoder(id)}", + method="PUT", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataAttribute, + construct_type( + type_=DataAttribute, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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) diff --git a/src/intercom/unstable/data_attributes/types/__init__.py b/src/intercom/unstable/data_attributes/types/__init__.py new file mode 100644 index 00000000..7ec71b76 --- /dev/null +++ b/src/intercom/unstable/data_attributes/types/__init__.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .data_attribute import DataAttribute + from .data_attribute_data_type import DataAttributeDataType + from .data_attribute_model import DataAttributeModel + from .lis_data_attributes_request_model import LisDataAttributesRequestModel +_dynamic_imports: typing.Dict[str, str] = { + "DataAttribute": ".data_attribute", + "DataAttributeDataType": ".data_attribute_data_type", + "DataAttributeModel": ".data_attribute_model", + "LisDataAttributesRequestModel": ".lis_data_attributes_request_model", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["DataAttribute", "DataAttributeDataType", "DataAttributeModel", "LisDataAttributesRequestModel"] diff --git a/src/intercom/unstable/data_attributes/types/data_attribute.py b/src/intercom/unstable/data_attributes/types/data_attribute.py new file mode 100644 index 00000000..ea8da9e9 --- /dev/null +++ b/src/intercom/unstable/data_attributes/types/data_attribute.py @@ -0,0 +1,109 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .data_attribute_data_type import DataAttributeDataType +from .data_attribute_model import DataAttributeModel + + +class DataAttribute(UncheckedBaseModel): + """ + Data Attributes are metadata used to describe your contact, company and conversation models. These include standard and custom attributes. By using the data attributes endpoint, you can get the global list of attributes for your workspace, as well as create and archive custom attributes. + """ + + type: typing.Optional[typing.Literal["data_attribute"]] = pydantic.Field(default=None) + """ + Value is `data_attribute`. + """ + + id: typing.Optional[int] = pydantic.Field(default=None) + """ + The unique identifier for the data attribute which is given by Intercom. Only available for custom attributes. + """ + + model: typing.Optional[DataAttributeModel] = pydantic.Field(default=None) + """ + Value is `contact` for user/lead attributes and `company` for company attributes. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Name of the attribute. + """ + + full_name: typing.Optional[str] = pydantic.Field(default=None) + """ + Full name of the attribute. Should match the name unless it's a nested attribute. We can split full_name on `.` to access nested user object values. + """ + + label: typing.Optional[str] = pydantic.Field(default=None) + """ + Readable name of the attribute (i.e. name you see in the UI) + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + Readable description of the attribute. + """ + + data_type: typing.Optional[DataAttributeDataType] = pydantic.Field(default=None) + """ + The data type of the attribute. + """ + + options: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + List of predefined options for attribute value. + """ + + api_writable: typing.Optional[bool] = pydantic.Field(default=None) + """ + Can this attribute be updated through API + """ + + messenger_writable: typing.Optional[bool] = pydantic.Field(default=None) + """ + Can this attribute be updated by the Messenger + """ + + ui_writable: typing.Optional[bool] = pydantic.Field(default=None) + """ + Can this attribute be updated in the UI + """ + + custom: typing.Optional[bool] = pydantic.Field(default=None) + """ + Set to true if this is a CDA + """ + + archived: typing.Optional[bool] = pydantic.Field(default=None) + """ + Is this attribute archived. (Only applicable to CDAs) + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the attribute was created as a UTC Unix timestamp + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the attribute was last updated as a UTC Unix timestamp + """ + + admin_id: typing.Optional[str] = pydantic.Field(default=None) + """ + Teammate who created the attribute. Only applicable to CDAs + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/data_attributes/types/data_attribute_data_type.py b/src/intercom/unstable/data_attributes/types/data_attribute_data_type.py new file mode 100644 index 00000000..f9c833fc --- /dev/null +++ b/src/intercom/unstable/data_attributes/types/data_attribute_data_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +DataAttributeDataType = typing.Union[typing.Literal["string", "integer", "float", "boolean", "date"], typing.Any] diff --git a/src/intercom/unstable/data_attributes/types/data_attribute_model.py b/src/intercom/unstable/data_attributes/types/data_attribute_model.py new file mode 100644 index 00000000..a16196e2 --- /dev/null +++ b/src/intercom/unstable/data_attributes/types/data_attribute_model.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +DataAttributeModel = typing.Union[typing.Literal["contact", "company"], typing.Any] diff --git a/src/intercom/unstable/data_attributes/types/lis_data_attributes_request_model.py b/src/intercom/unstable/data_attributes/types/lis_data_attributes_request_model.py new file mode 100644 index 00000000..abca22c5 --- /dev/null +++ b/src/intercom/unstable/data_attributes/types/lis_data_attributes_request_model.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +LisDataAttributesRequestModel = typing.Union[typing.Literal["contact", "company", "conversation"], typing.Any] diff --git a/src/intercom/unstable/data_events/__init__.py b/src/intercom/unstable/data_events/__init__.py new file mode 100644 index 00000000..c005d5af --- /dev/null +++ b/src/intercom/unstable/data_events/__init__.py @@ -0,0 +1,55 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + CreateDataEventSummariesRequestEventSummaries, + DataEvent, + LisDataEventsRequestFilter, + LisDataEventsRequestFilterEmail, + LisDataEventsRequestFilterIntercomUserId, + LisDataEventsRequestFilterUserId, + ) +_dynamic_imports: typing.Dict[str, str] = { + "CreateDataEventSummariesRequestEventSummaries": ".types", + "DataEvent": ".types", + "LisDataEventsRequestFilter": ".types", + "LisDataEventsRequestFilterEmail": ".types", + "LisDataEventsRequestFilterIntercomUserId": ".types", + "LisDataEventsRequestFilterUserId": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "CreateDataEventSummariesRequestEventSummaries", + "DataEvent", + "LisDataEventsRequestFilter", + "LisDataEventsRequestFilterEmail", + "LisDataEventsRequestFilterIntercomUserId", + "LisDataEventsRequestFilterUserId", +] diff --git a/src/intercom/unstable/data_events/client.py b/src/intercom/unstable/data_events/client.py new file mode 100644 index 00000000..99500eb6 --- /dev/null +++ b/src/intercom/unstable/data_events/client.py @@ -0,0 +1,414 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ...types.create_data_event_request_two import CreateDataEventRequestTwo +from ..types.data_event_summary import DataEventSummary +from .raw_client import AsyncRawDataEventsClient, RawDataEventsClient +from .types.create_data_event_summaries_request_event_summaries import CreateDataEventSummariesRequestEventSummaries +from .types.lis_data_events_request_filter import LisDataEventsRequestFilter + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class DataEventsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawDataEventsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawDataEventsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawDataEventsClient + """ + return self._raw_client + + def lis_data_events( + self, + *, + filter: LisDataEventsRequestFilter, + type: str, + summary: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> DataEventSummary: + """ + + > 🚧 + > + > Please note that you can only 'list' events that are less than 90 days old. Event counts and summaries will still include your events older than 90 days but you cannot 'list' these events individually if they are older than 90 days + + The events belonging to a customer can be listed by sending a GET request to `https://api.intercom.io/events` with a user or lead identifier along with a `type` parameter. The identifier parameter can be one of `user_id`, `email` or `intercom_user_id`. The `type` parameter value must be `user`. + + - `https://api.intercom.io/events?type=user&user_id={user_id}` + - `https://api.intercom.io/events?type=user&email={email}` + - `https://api.intercom.io/events?type=user&intercom_user_id={id}` (this call can be used to list leads) + + The `email` parameter value should be [url encoded](http://en.wikipedia.org/wiki/Percent-encoding) when sending. + + You can optionally define the result page size as well with the `per_page` parameter. + + Parameters + ---------- + filter : LisDataEventsRequestFilter + + type : str + The value must be user + + summary : typing.Optional[bool] + summary flag + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataEventSummary + Successful response + + Examples + -------- + from intercom import Intercom + from intercom.unstable.data_events import LisDataEventsRequestFilterUserId + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.data_events.lis_data_events( + filter=LisDataEventsRequestFilterUserId( + user_id="user_id", + ), + type="type", + ) + """ + _response = self._raw_client.lis_data_events( + filter=filter, type=type, summary=summary, request_options=request_options + ) + return _response.data + + def create_data_event( + self, *, request: CreateDataEventRequestTwo, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + + You will need an Access Token that has write permissions to send Events. Once you have a key you can submit events via POST to the Events resource, which is located at https://api.intercom.io/events, or you can send events using one of the client libraries. When working with the HTTP API directly a client should send the event with a `Content-Type` of `application/json`. + + When using the JavaScript API, [adding the code to your app](http://docs.intercom.io/configuring-Intercom/tracking-user-events-in-your-app) makes the Events API available. Once added, you can submit an event using the `trackEvent` method. This will associate the event with the Lead or currently logged-in user or logged-out visitor/lead and send it to Intercom. The final parameter is a map that can be used to send optional metadata about the event. + + With the Ruby client you pass a hash describing the event to `Intercom::Event.create`, or call the `track_user` method directly on the current user object (e.g. `user.track_event`). + + **NB: For the JSON object types, please note that we do not currently support nested JSON structure.** + + | Type | Description | Example | + | :-------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------- | + | String | The value is a JSON String | `"source":"desktop"` | + | Number | The value is a JSON Number | `"load": 3.67` | + | Date | The key ends with the String `_date` and the value is a [Unix timestamp](http://en.wikipedia.org/wiki/Unix_time), assumed to be in the [UTC](http://en.wikipedia.org/wiki/Coordinated_Universal_Time) timezone. | `"contact_date": 1392036272` | + | Link | The value is a HTTP or HTTPS URI. | `"article": "https://example.org/ab1de.html"` | + | Rich Link | The value is a JSON object that contains `url` and `value` keys. | `"article": {"url": "https://example.org/ab1de.html", "value":"the dude abides"}` | + | Monetary Amount | The value is a JSON object that contains `amount` and `currency` keys. The `amount` key is a positive integer representing the amount in cents. The price in the example to the right denotes €349.99. | `"price": {"amount": 34999, "currency": "eur"}` | + + **Lead Events** + + When submitting events for Leads, you will need to specify the Lead's `id`. + + **Metadata behaviour** + + - We currently limit the number of tracked metadata keys to 10 per event. Once the quota is reached, we ignore any further keys we receive. The first 10 metadata keys are determined by the order in which they are sent in with the event. + - It is not possible to change the metadata keys once the event has been sent. A new event will need to be created with the new keys and you can archive the old one. + - There might be up to 24 hrs delay when you send a new metadata for an existing event. + + **Event de-duplication** + + The API may detect and ignore duplicate events. Each event is uniquely identified as a combination of the following data - the Workspace identifier, the Contact external identifier, the Data Event name and the Data Event created time. As a result, it is **strongly recommended** to send a second granularity Unix timestamp in the `created_at` field. + + Duplicated events are responded to using the normal `202 Accepted` code - an error is not thrown, however repeat requests will be counted against any rate limit that is in place. + + ### HTTP API Responses + + - Successful responses to submitted events return `202 Accepted` with an empty body. + - Unauthorised access will be rejected with a `401 Unauthorized` or `403 Forbidden` response code. + - Events sent about users that cannot be found will return a `404 Not Found`. + - Event lists containing duplicate events will have those duplicates ignored. + - Server errors will return a `500` response code and may contain an error message in the body. + + Parameters + ---------- + request : CreateDataEventRequestTwo + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.data_events.create_data_event( + request={"key": "value"}, + ) + """ + _response = self._raw_client.create_data_event(request=request, request_options=request_options) + return _response.data + + def data_event_summaries( + self, + *, + user_id: typing.Optional[str] = OMIT, + event_summaries: typing.Optional[CreateDataEventSummariesRequestEventSummaries] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> None: + """ + Create event summaries for a user. Event summaries are used to track the number of times an event has occurred, the first time it occurred and the last time it occurred. + + Parameters + ---------- + user_id : typing.Optional[str] + Your identifier for the user. + + event_summaries : typing.Optional[CreateDataEventSummariesRequestEventSummaries] + A list of event summaries for the user. Each event summary should contain the event name, the time the event occurred, and the number of times the event occurred. The event name should be a past tense 'verb-noun' combination, to improve readability, for example `updated-plan`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.data_events.data_event_summaries() + """ + _response = self._raw_client.data_event_summaries( + user_id=user_id, event_summaries=event_summaries, request_options=request_options + ) + return _response.data + + +class AsyncDataEventsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawDataEventsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawDataEventsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawDataEventsClient + """ + return self._raw_client + + async def lis_data_events( + self, + *, + filter: LisDataEventsRequestFilter, + type: str, + summary: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> DataEventSummary: + """ + + > 🚧 + > + > Please note that you can only 'list' events that are less than 90 days old. Event counts and summaries will still include your events older than 90 days but you cannot 'list' these events individually if they are older than 90 days + + The events belonging to a customer can be listed by sending a GET request to `https://api.intercom.io/events` with a user or lead identifier along with a `type` parameter. The identifier parameter can be one of `user_id`, `email` or `intercom_user_id`. The `type` parameter value must be `user`. + + - `https://api.intercom.io/events?type=user&user_id={user_id}` + - `https://api.intercom.io/events?type=user&email={email}` + - `https://api.intercom.io/events?type=user&intercom_user_id={id}` (this call can be used to list leads) + + The `email` parameter value should be [url encoded](http://en.wikipedia.org/wiki/Percent-encoding) when sending. + + You can optionally define the result page size as well with the `per_page` parameter. + + Parameters + ---------- + filter : LisDataEventsRequestFilter + + type : str + The value must be user + + summary : typing.Optional[bool] + summary flag + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataEventSummary + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.unstable.data_events import LisDataEventsRequestFilterUserId + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.data_events.lis_data_events( + filter=LisDataEventsRequestFilterUserId( + user_id="user_id", + ), + type="type", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.lis_data_events( + filter=filter, type=type, summary=summary, request_options=request_options + ) + return _response.data + + async def create_data_event( + self, *, request: CreateDataEventRequestTwo, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + + You will need an Access Token that has write permissions to send Events. Once you have a key you can submit events via POST to the Events resource, which is located at https://api.intercom.io/events, or you can send events using one of the client libraries. When working with the HTTP API directly a client should send the event with a `Content-Type` of `application/json`. + + When using the JavaScript API, [adding the code to your app](http://docs.intercom.io/configuring-Intercom/tracking-user-events-in-your-app) makes the Events API available. Once added, you can submit an event using the `trackEvent` method. This will associate the event with the Lead or currently logged-in user or logged-out visitor/lead and send it to Intercom. The final parameter is a map that can be used to send optional metadata about the event. + + With the Ruby client you pass a hash describing the event to `Intercom::Event.create`, or call the `track_user` method directly on the current user object (e.g. `user.track_event`). + + **NB: For the JSON object types, please note that we do not currently support nested JSON structure.** + + | Type | Description | Example | + | :-------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------- | + | String | The value is a JSON String | `"source":"desktop"` | + | Number | The value is a JSON Number | `"load": 3.67` | + | Date | The key ends with the String `_date` and the value is a [Unix timestamp](http://en.wikipedia.org/wiki/Unix_time), assumed to be in the [UTC](http://en.wikipedia.org/wiki/Coordinated_Universal_Time) timezone. | `"contact_date": 1392036272` | + | Link | The value is a HTTP or HTTPS URI. | `"article": "https://example.org/ab1de.html"` | + | Rich Link | The value is a JSON object that contains `url` and `value` keys. | `"article": {"url": "https://example.org/ab1de.html", "value":"the dude abides"}` | + | Monetary Amount | The value is a JSON object that contains `amount` and `currency` keys. The `amount` key is a positive integer representing the amount in cents. The price in the example to the right denotes €349.99. | `"price": {"amount": 34999, "currency": "eur"}` | + + **Lead Events** + + When submitting events for Leads, you will need to specify the Lead's `id`. + + **Metadata behaviour** + + - We currently limit the number of tracked metadata keys to 10 per event. Once the quota is reached, we ignore any further keys we receive. The first 10 metadata keys are determined by the order in which they are sent in with the event. + - It is not possible to change the metadata keys once the event has been sent. A new event will need to be created with the new keys and you can archive the old one. + - There might be up to 24 hrs delay when you send a new metadata for an existing event. + + **Event de-duplication** + + The API may detect and ignore duplicate events. Each event is uniquely identified as a combination of the following data - the Workspace identifier, the Contact external identifier, the Data Event name and the Data Event created time. As a result, it is **strongly recommended** to send a second granularity Unix timestamp in the `created_at` field. + + Duplicated events are responded to using the normal `202 Accepted` code - an error is not thrown, however repeat requests will be counted against any rate limit that is in place. + + ### HTTP API Responses + + - Successful responses to submitted events return `202 Accepted` with an empty body. + - Unauthorised access will be rejected with a `401 Unauthorized` or `403 Forbidden` response code. + - Events sent about users that cannot be found will return a `404 Not Found`. + - Event lists containing duplicate events will have those duplicates ignored. + - Server errors will return a `500` response code and may contain an error message in the body. + + Parameters + ---------- + request : CreateDataEventRequestTwo + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.data_events.create_data_event( + request={"key": "value"}, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_data_event(request=request, request_options=request_options) + return _response.data + + async def data_event_summaries( + self, + *, + user_id: typing.Optional[str] = OMIT, + event_summaries: typing.Optional[CreateDataEventSummariesRequestEventSummaries] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> None: + """ + Create event summaries for a user. Event summaries are used to track the number of times an event has occurred, the first time it occurred and the last time it occurred. + + Parameters + ---------- + user_id : typing.Optional[str] + Your identifier for the user. + + event_summaries : typing.Optional[CreateDataEventSummariesRequestEventSummaries] + A list of event summaries for the user. Each event summary should contain the event name, the time the event occurred, and the number of times the event occurred. The event name should be a past tense 'verb-noun' combination, to improve readability, for example `updated-plan`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.data_events.data_event_summaries() + + + asyncio.run(main()) + """ + _response = await self._raw_client.data_event_summaries( + user_id=user_id, event_summaries=event_summaries, request_options=request_options + ) + return _response.data diff --git a/src/intercom/unstable/data_events/raw_client.py b/src/intercom/unstable/data_events/raw_client.py new file mode 100644 index 00000000..8cb28e73 --- /dev/null +++ b/src/intercom/unstable/data_events/raw_client.py @@ -0,0 +1,480 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.http_response import AsyncHttpResponse, HttpResponse +from ...core.request_options import RequestOptions +from ...core.serialization import convert_and_respect_annotation_metadata +from ...core.unchecked_base_model import construct_type +from ...types.create_data_event_request_two import CreateDataEventRequestTwo +from ..errors.unauthorized_error import UnauthorizedError +from ..types.data_event_summary import DataEventSummary +from ..types.error import Error +from .types.create_data_event_summaries_request_event_summaries import CreateDataEventSummariesRequestEventSummaries +from .types.lis_data_events_request_filter import LisDataEventsRequestFilter + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawDataEventsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def lis_data_events( + self, + *, + filter: LisDataEventsRequestFilter, + type: str, + summary: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[DataEventSummary]: + """ + + > 🚧 + > + > Please note that you can only 'list' events that are less than 90 days old. Event counts and summaries will still include your events older than 90 days but you cannot 'list' these events individually if they are older than 90 days + + The events belonging to a customer can be listed by sending a GET request to `https://api.intercom.io/events` with a user or lead identifier along with a `type` parameter. The identifier parameter can be one of `user_id`, `email` or `intercom_user_id`. The `type` parameter value must be `user`. + + - `https://api.intercom.io/events?type=user&user_id={user_id}` + - `https://api.intercom.io/events?type=user&email={email}` + - `https://api.intercom.io/events?type=user&intercom_user_id={id}` (this call can be used to list leads) + + The `email` parameter value should be [url encoded](http://en.wikipedia.org/wiki/Percent-encoding) when sending. + + You can optionally define the result page size as well with the `per_page` parameter. + + Parameters + ---------- + filter : LisDataEventsRequestFilter + + type : str + The value must be user + + summary : typing.Optional[bool] + summary flag + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DataEventSummary] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "events", + method="GET", + params={ + "filter": convert_and_respect_annotation_metadata( + object_=filter, annotation=LisDataEventsRequestFilter, direction="write" + ), + "type": type, + "summary": summary, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataEventSummary, + construct_type( + type_=DataEventSummary, # 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 create_data_event( + self, *, request: CreateDataEventRequestTwo, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[None]: + """ + + You will need an Access Token that has write permissions to send Events. Once you have a key you can submit events via POST to the Events resource, which is located at https://api.intercom.io/events, or you can send events using one of the client libraries. When working with the HTTP API directly a client should send the event with a `Content-Type` of `application/json`. + + When using the JavaScript API, [adding the code to your app](http://docs.intercom.io/configuring-Intercom/tracking-user-events-in-your-app) makes the Events API available. Once added, you can submit an event using the `trackEvent` method. This will associate the event with the Lead or currently logged-in user or logged-out visitor/lead and send it to Intercom. The final parameter is a map that can be used to send optional metadata about the event. + + With the Ruby client you pass a hash describing the event to `Intercom::Event.create`, or call the `track_user` method directly on the current user object (e.g. `user.track_event`). + + **NB: For the JSON object types, please note that we do not currently support nested JSON structure.** + + | Type | Description | Example | + | :-------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------- | + | String | The value is a JSON String | `"source":"desktop"` | + | Number | The value is a JSON Number | `"load": 3.67` | + | Date | The key ends with the String `_date` and the value is a [Unix timestamp](http://en.wikipedia.org/wiki/Unix_time), assumed to be in the [UTC](http://en.wikipedia.org/wiki/Coordinated_Universal_Time) timezone. | `"contact_date": 1392036272` | + | Link | The value is a HTTP or HTTPS URI. | `"article": "https://example.org/ab1de.html"` | + | Rich Link | The value is a JSON object that contains `url` and `value` keys. | `"article": {"url": "https://example.org/ab1de.html", "value":"the dude abides"}` | + | Monetary Amount | The value is a JSON object that contains `amount` and `currency` keys. The `amount` key is a positive integer representing the amount in cents. The price in the example to the right denotes €349.99. | `"price": {"amount": 34999, "currency": "eur"}` | + + **Lead Events** + + When submitting events for Leads, you will need to specify the Lead's `id`. + + **Metadata behaviour** + + - We currently limit the number of tracked metadata keys to 10 per event. Once the quota is reached, we ignore any further keys we receive. The first 10 metadata keys are determined by the order in which they are sent in with the event. + - It is not possible to change the metadata keys once the event has been sent. A new event will need to be created with the new keys and you can archive the old one. + - There might be up to 24 hrs delay when you send a new metadata for an existing event. + + **Event de-duplication** + + The API may detect and ignore duplicate events. Each event is uniquely identified as a combination of the following data - the Workspace identifier, the Contact external identifier, the Data Event name and the Data Event created time. As a result, it is **strongly recommended** to send a second granularity Unix timestamp in the `created_at` field. + + Duplicated events are responded to using the normal `202 Accepted` code - an error is not thrown, however repeat requests will be counted against any rate limit that is in place. + + ### HTTP API Responses + + - Successful responses to submitted events return `202 Accepted` with an empty body. + - Unauthorised access will be rejected with a `401 Unauthorized` or `403 Forbidden` response code. + - Events sent about users that cannot be found will return a `404 Not Found`. + - Event lists containing duplicate events will have those duplicates ignored. + - Server errors will return a `500` response code and may contain an error message in the body. + + Parameters + ---------- + request : CreateDataEventRequestTwo + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + "events", + method="POST", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + 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 data_event_summaries( + self, + *, + user_id: typing.Optional[str] = OMIT, + event_summaries: typing.Optional[CreateDataEventSummariesRequestEventSummaries] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[None]: + """ + Create event summaries for a user. Event summaries are used to track the number of times an event has occurred, the first time it occurred and the last time it occurred. + + Parameters + ---------- + user_id : typing.Optional[str] + Your identifier for the user. + + event_summaries : typing.Optional[CreateDataEventSummariesRequestEventSummaries] + A list of event summaries for the user. Each event summary should contain the event name, the time the event occurred, and the number of times the event occurred. The event name should be a past tense 'verb-noun' combination, to improve readability, for example `updated-plan`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + "events/summaries", + method="POST", + json={ + "user_id": user_id, + "event_summaries": convert_and_respect_annotation_metadata( + object_=event_summaries, annotation=CreateDataEventSummariesRequestEventSummaries, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + 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) + + +class AsyncRawDataEventsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def lis_data_events( + self, + *, + filter: LisDataEventsRequestFilter, + type: str, + summary: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[DataEventSummary]: + """ + + > 🚧 + > + > Please note that you can only 'list' events that are less than 90 days old. Event counts and summaries will still include your events older than 90 days but you cannot 'list' these events individually if they are older than 90 days + + The events belonging to a customer can be listed by sending a GET request to `https://api.intercom.io/events` with a user or lead identifier along with a `type` parameter. The identifier parameter can be one of `user_id`, `email` or `intercom_user_id`. The `type` parameter value must be `user`. + + - `https://api.intercom.io/events?type=user&user_id={user_id}` + - `https://api.intercom.io/events?type=user&email={email}` + - `https://api.intercom.io/events?type=user&intercom_user_id={id}` (this call can be used to list leads) + + The `email` parameter value should be [url encoded](http://en.wikipedia.org/wiki/Percent-encoding) when sending. + + You can optionally define the result page size as well with the `per_page` parameter. + + Parameters + ---------- + filter : LisDataEventsRequestFilter + + type : str + The value must be user + + summary : typing.Optional[bool] + summary flag + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DataEventSummary] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "events", + method="GET", + params={ + "filter": convert_and_respect_annotation_metadata( + object_=filter, annotation=LisDataEventsRequestFilter, direction="write" + ), + "type": type, + "summary": summary, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataEventSummary, + construct_type( + type_=DataEventSummary, # 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 create_data_event( + self, *, request: CreateDataEventRequestTwo, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + + You will need an Access Token that has write permissions to send Events. Once you have a key you can submit events via POST to the Events resource, which is located at https://api.intercom.io/events, or you can send events using one of the client libraries. When working with the HTTP API directly a client should send the event with a `Content-Type` of `application/json`. + + When using the JavaScript API, [adding the code to your app](http://docs.intercom.io/configuring-Intercom/tracking-user-events-in-your-app) makes the Events API available. Once added, you can submit an event using the `trackEvent` method. This will associate the event with the Lead or currently logged-in user or logged-out visitor/lead and send it to Intercom. The final parameter is a map that can be used to send optional metadata about the event. + + With the Ruby client you pass a hash describing the event to `Intercom::Event.create`, or call the `track_user` method directly on the current user object (e.g. `user.track_event`). + + **NB: For the JSON object types, please note that we do not currently support nested JSON structure.** + + | Type | Description | Example | + | :-------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------- | + | String | The value is a JSON String | `"source":"desktop"` | + | Number | The value is a JSON Number | `"load": 3.67` | + | Date | The key ends with the String `_date` and the value is a [Unix timestamp](http://en.wikipedia.org/wiki/Unix_time), assumed to be in the [UTC](http://en.wikipedia.org/wiki/Coordinated_Universal_Time) timezone. | `"contact_date": 1392036272` | + | Link | The value is a HTTP or HTTPS URI. | `"article": "https://example.org/ab1de.html"` | + | Rich Link | The value is a JSON object that contains `url` and `value` keys. | `"article": {"url": "https://example.org/ab1de.html", "value":"the dude abides"}` | + | Monetary Amount | The value is a JSON object that contains `amount` and `currency` keys. The `amount` key is a positive integer representing the amount in cents. The price in the example to the right denotes €349.99. | `"price": {"amount": 34999, "currency": "eur"}` | + + **Lead Events** + + When submitting events for Leads, you will need to specify the Lead's `id`. + + **Metadata behaviour** + + - We currently limit the number of tracked metadata keys to 10 per event. Once the quota is reached, we ignore any further keys we receive. The first 10 metadata keys are determined by the order in which they are sent in with the event. + - It is not possible to change the metadata keys once the event has been sent. A new event will need to be created with the new keys and you can archive the old one. + - There might be up to 24 hrs delay when you send a new metadata for an existing event. + + **Event de-duplication** + + The API may detect and ignore duplicate events. Each event is uniquely identified as a combination of the following data - the Workspace identifier, the Contact external identifier, the Data Event name and the Data Event created time. As a result, it is **strongly recommended** to send a second granularity Unix timestamp in the `created_at` field. + + Duplicated events are responded to using the normal `202 Accepted` code - an error is not thrown, however repeat requests will be counted against any rate limit that is in place. + + ### HTTP API Responses + + - Successful responses to submitted events return `202 Accepted` with an empty body. + - Unauthorised access will be rejected with a `401 Unauthorized` or `403 Forbidden` response code. + - Events sent about users that cannot be found will return a `404 Not Found`. + - Event lists containing duplicate events will have those duplicates ignored. + - Server errors will return a `500` response code and may contain an error message in the body. + + Parameters + ---------- + request : CreateDataEventRequestTwo + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + "events", + method="POST", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + 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 data_event_summaries( + self, + *, + user_id: typing.Optional[str] = OMIT, + event_summaries: typing.Optional[CreateDataEventSummariesRequestEventSummaries] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[None]: + """ + Create event summaries for a user. Event summaries are used to track the number of times an event has occurred, the first time it occurred and the last time it occurred. + + Parameters + ---------- + user_id : typing.Optional[str] + Your identifier for the user. + + event_summaries : typing.Optional[CreateDataEventSummariesRequestEventSummaries] + A list of event summaries for the user. Each event summary should contain the event name, the time the event occurred, and the number of times the event occurred. The event name should be a past tense 'verb-noun' combination, to improve readability, for example `updated-plan`. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + "events/summaries", + method="POST", + json={ + "user_id": user_id, + "event_summaries": convert_and_respect_annotation_metadata( + object_=event_summaries, annotation=CreateDataEventSummariesRequestEventSummaries, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + 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) diff --git a/src/intercom/unstable/data_events/types/__init__.py b/src/intercom/unstable/data_events/types/__init__.py new file mode 100644 index 00000000..4fd0dc06 --- /dev/null +++ b/src/intercom/unstable/data_events/types/__init__.py @@ -0,0 +1,53 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .create_data_event_summaries_request_event_summaries import CreateDataEventSummariesRequestEventSummaries + from .data_event import DataEvent + from .lis_data_events_request_filter import LisDataEventsRequestFilter + from .lis_data_events_request_filter_email import LisDataEventsRequestFilterEmail + from .lis_data_events_request_filter_intercom_user_id import LisDataEventsRequestFilterIntercomUserId + from .lis_data_events_request_filter_user_id import LisDataEventsRequestFilterUserId +_dynamic_imports: typing.Dict[str, str] = { + "CreateDataEventSummariesRequestEventSummaries": ".create_data_event_summaries_request_event_summaries", + "DataEvent": ".data_event", + "LisDataEventsRequestFilter": ".lis_data_events_request_filter", + "LisDataEventsRequestFilterEmail": ".lis_data_events_request_filter_email", + "LisDataEventsRequestFilterIntercomUserId": ".lis_data_events_request_filter_intercom_user_id", + "LisDataEventsRequestFilterUserId": ".lis_data_events_request_filter_user_id", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "CreateDataEventSummariesRequestEventSummaries", + "DataEvent", + "LisDataEventsRequestFilter", + "LisDataEventsRequestFilterEmail", + "LisDataEventsRequestFilterIntercomUserId", + "LisDataEventsRequestFilterUserId", +] diff --git a/src/intercom/unstable/data_events/types/create_data_event_summaries_request_event_summaries.py b/src/intercom/unstable/data_events/types/create_data_event_summaries_request_event_summaries.py new file mode 100644 index 00000000..e41c16fe --- /dev/null +++ b/src/intercom/unstable/data_events/types/create_data_event_summaries_request_event_summaries.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class CreateDataEventSummariesRequestEventSummaries(UncheckedBaseModel): + """ + A list of event summaries for the user. Each event summary should contain the event name, the time the event occurred, and the number of times the event occurred. The event name should be a past tense 'verb-noun' combination, to improve readability, for example `updated-plan`. + """ + + event_name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the event that occurred. A good event name is typically a past tense 'verb-noun' combination, to improve readability, for example `updated-plan`. + """ + + count: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of times the event occurred. + """ + + first: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time the event was sent + """ + + last: typing.Optional[int] = pydantic.Field(default=None) + """ + The last time the event was sent + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/data_events/types/data_event.py b/src/intercom/unstable/data_events/types/data_event.py new file mode 100644 index 00000000..a3589fc0 --- /dev/null +++ b/src/intercom/unstable/data_events/types/data_event.py @@ -0,0 +1,62 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class DataEvent(UncheckedBaseModel): + """ + Data events are used to notify Intercom of changes to your data. + """ + + type: typing.Optional[typing.Literal["event"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + event_name: str = pydantic.Field() + """ + The name of the event that occurred. This is presented to your App's admins when filtering and creating segments - a good event name is typically a past tense 'verb-noun' combination, to improve readability, for example `updated-plan`. + """ + + created_at: int = pydantic.Field() + """ + The time the event occurred as a UTC Unix timestamp + """ + + user_id: typing.Optional[str] = pydantic.Field(default=None) + """ + Your identifier for the user. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + Your identifier for a lead or a user. + """ + + intercom_user_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom identifier for the user. + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + An email address for your user. An email should only be used where your application uses email to uniquely identify users. + """ + + metadata: typing.Optional[typing.Dict[str, str]] = pydantic.Field(default=None) + """ + Optional metadata about the event. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/data_events/types/lis_data_events_request_filter.py b/src/intercom/unstable/data_events/types/lis_data_events_request_filter.py new file mode 100644 index 00000000..ee667dc6 --- /dev/null +++ b/src/intercom/unstable/data_events/types/lis_data_events_request_filter.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .lis_data_events_request_filter_email import LisDataEventsRequestFilterEmail +from .lis_data_events_request_filter_intercom_user_id import LisDataEventsRequestFilterIntercomUserId +from .lis_data_events_request_filter_user_id import LisDataEventsRequestFilterUserId + +LisDataEventsRequestFilter = typing.Union[ + LisDataEventsRequestFilterUserId, LisDataEventsRequestFilterIntercomUserId, LisDataEventsRequestFilterEmail +] diff --git a/src/intercom/unstable/data_events/types/lis_data_events_request_filter_email.py b/src/intercom/unstable/data_events/types/lis_data_events_request_filter_email.py new file mode 100644 index 00000000..c6baa27b --- /dev/null +++ b/src/intercom/unstable/data_events/types/lis_data_events_request_filter_email.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class LisDataEventsRequestFilterEmail(UncheckedBaseModel): + email: str + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/data_events/types/lis_data_events_request_filter_intercom_user_id.py b/src/intercom/unstable/data_events/types/lis_data_events_request_filter_intercom_user_id.py new file mode 100644 index 00000000..cc50e58c --- /dev/null +++ b/src/intercom/unstable/data_events/types/lis_data_events_request_filter_intercom_user_id.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class LisDataEventsRequestFilterIntercomUserId(UncheckedBaseModel): + intercom_user_id: str + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/data_events/types/lis_data_events_request_filter_user_id.py b/src/intercom/unstable/data_events/types/lis_data_events_request_filter_user_id.py new file mode 100644 index 00000000..4a2ff737 --- /dev/null +++ b/src/intercom/unstable/data_events/types/lis_data_events_request_filter_user_id.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class LisDataEventsRequestFilterUserId(UncheckedBaseModel): + user_id: str + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/data_export/__init__.py b/src/intercom/unstable/data_export/__init__.py new file mode 100644 index 00000000..b5b1ba20 --- /dev/null +++ b/src/intercom/unstable/data_export/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import DataExport, DataExportStatus +_dynamic_imports: typing.Dict[str, str] = {"DataExport": ".types", "DataExportStatus": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["DataExport", "DataExportStatus"] diff --git a/src/intercom/unstable/data_export/client.py b/src/intercom/unstable/data_export/client.py new file mode 100644 index 00000000..ad84b109 --- /dev/null +++ b/src/intercom/unstable/data_export/client.py @@ -0,0 +1,397 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from .raw_client import AsyncRawDataExportClient, RawDataExportClient +from .types.data_export import DataExport + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class DataExportClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawDataExportClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawDataExportClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawDataExportClient + """ + return self._raw_client + + def create_data_export( + self, *, created_at_after: int, created_at_before: int, request_options: typing.Optional[RequestOptions] = None + ) -> DataExport: + """ + To create your export job, you need to send a `POST` request to the export endpoint `https://api.intercom.io/export/content/data`. + + The only parameters you need to provide are the range of dates that you want exported. + + >🚧 Limit of one active job + > + > You can only have one active job per workspace. You will receive a HTTP status code of 429 with the message Exceeded rate limit of 1 pending message data export jobs if you attempt to create a second concurrent job. + + >❗️ Updated_at not included + > + > It should be noted that the timeframe only includes messages sent during the time period and not messages that were only updated during this period. For example, if a message was updated yesterday but sent two days ago, you would need to set the created_at_after date before the message was sent to include that in your retrieval job. + + >📘 Date ranges are inclusive + > + > Requesting data for 2018-06-01 until 2018-06-30 will get all data for those days including those specified - e.g. 2018-06-01 00:00:00 until 2018-06-30 23:59:99. + + Parameters + ---------- + created_at_after : int + The start date that you request data for. It must be formatted as a unix timestamp. + + created_at_before : int + The end date that you request data for. It must be formatted as a unix timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataExport + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.data_export.create_data_export( + created_at_after=1734519776, + created_at_before=1734537776, + ) + """ + _response = self._raw_client.create_data_export( + created_at_after=created_at_after, created_at_before=created_at_before, request_options=request_options + ) + return _response.data + + def get_data_export( + self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DataExport: + """ + You can view the status of your job by sending a `GET` request to the URL + `https://api.intercom.io/export/content/data/{job_identifier}` - the `{job_identifier}` is the value returned in the response when you first created the export job. More on it can be seen in the Export Job Model. + + > 🚧 Jobs expire after two days + > All jobs that have completed processing (and are thus available to download from the provided URL) will have an expiry limit of two days from when the export ob completed. After this, the data will no longer be available. + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataExport + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.data_export.get_data_export( + job_identifier="job_identifier", + ) + """ + _response = self._raw_client.get_data_export(job_identifier, request_options=request_options) + return _response.data + + def cancel_data_export( + self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DataExport: + """ + You can cancel your job + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataExport + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.data_export.cancel_data_export( + job_identifier="job_identifier", + ) + """ + _response = self._raw_client.cancel_data_export(job_identifier, request_options=request_options) + return _response.data + + def download_data_export( + self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + When a job has a status of complete, and thus a filled download_url, you can download your data by hitting that provided URL, formatted like so: https://api.intercom.io/download/content/data/xyz1234. + + Your exported message data will be streamed continuously back down to you in a gzipped CSV format. + + > 📘 Octet header required + > + > You will have to specify the header Accept: `application/octet-stream` when hitting this endpoint. + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.data_export.download_data_export( + job_identifier="job_identifier", + ) + """ + _response = self._raw_client.download_data_export(job_identifier, request_options=request_options) + return _response.data + + +class AsyncDataExportClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawDataExportClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawDataExportClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawDataExportClient + """ + return self._raw_client + + async def create_data_export( + self, *, created_at_after: int, created_at_before: int, request_options: typing.Optional[RequestOptions] = None + ) -> DataExport: + """ + To create your export job, you need to send a `POST` request to the export endpoint `https://api.intercom.io/export/content/data`. + + The only parameters you need to provide are the range of dates that you want exported. + + >🚧 Limit of one active job + > + > You can only have one active job per workspace. You will receive a HTTP status code of 429 with the message Exceeded rate limit of 1 pending message data export jobs if you attempt to create a second concurrent job. + + >❗️ Updated_at not included + > + > It should be noted that the timeframe only includes messages sent during the time period and not messages that were only updated during this period. For example, if a message was updated yesterday but sent two days ago, you would need to set the created_at_after date before the message was sent to include that in your retrieval job. + + >📘 Date ranges are inclusive + > + > Requesting data for 2018-06-01 until 2018-06-30 will get all data for those days including those specified - e.g. 2018-06-01 00:00:00 until 2018-06-30 23:59:99. + + Parameters + ---------- + created_at_after : int + The start date that you request data for. It must be formatted as a unix timestamp. + + created_at_before : int + The end date that you request data for. It must be formatted as a unix timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataExport + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.data_export.create_data_export( + created_at_after=1734519776, + created_at_before=1734537776, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_data_export( + created_at_after=created_at_after, created_at_before=created_at_before, request_options=request_options + ) + return _response.data + + async def get_data_export( + self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DataExport: + """ + You can view the status of your job by sending a `GET` request to the URL + `https://api.intercom.io/export/content/data/{job_identifier}` - the `{job_identifier}` is the value returned in the response when you first created the export job. More on it can be seen in the Export Job Model. + + > 🚧 Jobs expire after two days + > All jobs that have completed processing (and are thus available to download from the provided URL) will have an expiry limit of two days from when the export ob completed. After this, the data will no longer be available. + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataExport + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.data_export.get_data_export( + job_identifier="job_identifier", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_data_export(job_identifier, request_options=request_options) + return _response.data + + async def cancel_data_export( + self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DataExport: + """ + You can cancel your job + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DataExport + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.data_export.cancel_data_export( + job_identifier="job_identifier", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.cancel_data_export(job_identifier, request_options=request_options) + return _response.data + + async def download_data_export( + self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + When a job has a status of complete, and thus a filled download_url, you can download your data by hitting that provided URL, formatted like so: https://api.intercom.io/download/content/data/xyz1234. + + Your exported message data will be streamed continuously back down to you in a gzipped CSV format. + + > 📘 Octet header required + > + > You will have to specify the header Accept: `application/octet-stream` when hitting this endpoint. + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.data_export.download_data_export( + job_identifier="job_identifier", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.download_data_export(job_identifier, request_options=request_options) + return _response.data diff --git a/src/intercom/unstable/data_export/raw_client.py b/src/intercom/unstable/data_export/raw_client.py new file mode 100644 index 00000000..0e160b39 --- /dev/null +++ b/src/intercom/unstable/data_export/raw_client.py @@ -0,0 +1,391 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from .types.data_export import DataExport + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawDataExportClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def create_data_export( + self, *, created_at_after: int, created_at_before: int, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DataExport]: + """ + To create your export job, you need to send a `POST` request to the export endpoint `https://api.intercom.io/export/content/data`. + + The only parameters you need to provide are the range of dates that you want exported. + + >🚧 Limit of one active job + > + > You can only have one active job per workspace. You will receive a HTTP status code of 429 with the message Exceeded rate limit of 1 pending message data export jobs if you attempt to create a second concurrent job. + + >❗️ Updated_at not included + > + > It should be noted that the timeframe only includes messages sent during the time period and not messages that were only updated during this period. For example, if a message was updated yesterday but sent two days ago, you would need to set the created_at_after date before the message was sent to include that in your retrieval job. + + >📘 Date ranges are inclusive + > + > Requesting data for 2018-06-01 until 2018-06-30 will get all data for those days including those specified - e.g. 2018-06-01 00:00:00 until 2018-06-30 23:59:99. + + Parameters + ---------- + created_at_after : int + The start date that you request data for. It must be formatted as a unix timestamp. + + created_at_before : int + The end date that you request data for. It must be formatted as a unix timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DataExport] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "export/content/data", + method="POST", + json={ + "created_at_after": created_at_after, + "created_at_before": created_at_before, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataExport, + construct_type( + type_=DataExport, # 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 get_data_export( + self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DataExport]: + """ + You can view the status of your job by sending a `GET` request to the URL + `https://api.intercom.io/export/content/data/{job_identifier}` - the `{job_identifier}` is the value returned in the response when you first created the export job. More on it can be seen in the Export Job Model. + + > 🚧 Jobs expire after two days + > All jobs that have completed processing (and are thus available to download from the provided URL) will have an expiry limit of two days from when the export ob completed. After this, the data will no longer be available. + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DataExport] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"export/content/data/{jsonable_encoder(job_identifier)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataExport, + construct_type( + type_=DataExport, # 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 cancel_data_export( + self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DataExport]: + """ + You can cancel your job + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DataExport] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"export/cancel/{jsonable_encoder(job_identifier)}", + method="POST", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataExport, + construct_type( + type_=DataExport, # 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 download_data_export( + self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[None]: + """ + When a job has a status of complete, and thus a filled download_url, you can download your data by hitting that provided URL, formatted like so: https://api.intercom.io/download/content/data/xyz1234. + + Your exported message data will be streamed continuously back down to you in a gzipped CSV format. + + > 📘 Octet header required + > + > You will have to specify the header Accept: `application/octet-stream` when hitting this endpoint. + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + f"download/content/data/{jsonable_encoder(job_identifier)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + _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 AsyncRawDataExportClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def create_data_export( + self, *, created_at_after: int, created_at_before: int, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DataExport]: + """ + To create your export job, you need to send a `POST` request to the export endpoint `https://api.intercom.io/export/content/data`. + + The only parameters you need to provide are the range of dates that you want exported. + + >🚧 Limit of one active job + > + > You can only have one active job per workspace. You will receive a HTTP status code of 429 with the message Exceeded rate limit of 1 pending message data export jobs if you attempt to create a second concurrent job. + + >❗️ Updated_at not included + > + > It should be noted that the timeframe only includes messages sent during the time period and not messages that were only updated during this period. For example, if a message was updated yesterday but sent two days ago, you would need to set the created_at_after date before the message was sent to include that in your retrieval job. + + >📘 Date ranges are inclusive + > + > Requesting data for 2018-06-01 until 2018-06-30 will get all data for those days including those specified - e.g. 2018-06-01 00:00:00 until 2018-06-30 23:59:99. + + Parameters + ---------- + created_at_after : int + The start date that you request data for. It must be formatted as a unix timestamp. + + created_at_before : int + The end date that you request data for. It must be formatted as a unix timestamp. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DataExport] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "export/content/data", + method="POST", + json={ + "created_at_after": created_at_after, + "created_at_before": created_at_before, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataExport, + construct_type( + type_=DataExport, # 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 get_data_export( + self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DataExport]: + """ + You can view the status of your job by sending a `GET` request to the URL + `https://api.intercom.io/export/content/data/{job_identifier}` - the `{job_identifier}` is the value returned in the response when you first created the export job. More on it can be seen in the Export Job Model. + + > 🚧 Jobs expire after two days + > All jobs that have completed processing (and are thus available to download from the provided URL) will have an expiry limit of two days from when the export ob completed. After this, the data will no longer be available. + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DataExport] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"export/content/data/{jsonable_encoder(job_identifier)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataExport, + construct_type( + type_=DataExport, # 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 cancel_data_export( + self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DataExport]: + """ + You can cancel your job + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DataExport] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"export/cancel/{jsonable_encoder(job_identifier)}", + method="POST", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DataExport, + construct_type( + type_=DataExport, # 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 download_data_export( + self, job_identifier: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + When a job has a status of complete, and thus a filled download_url, you can download your data by hitting that provided URL, formatted like so: https://api.intercom.io/download/content/data/xyz1234. + + Your exported message data will be streamed continuously back down to you in a gzipped CSV format. + + > 📘 Octet header required + > + > You will have to specify the header Accept: `application/octet-stream` when hitting this endpoint. + + Parameters + ---------- + job_identifier : str + job_identifier + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + f"download/content/data/{jsonable_encoder(job_identifier)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + _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) diff --git a/src/intercom/unstable/data_export/types/__init__.py b/src/intercom/unstable/data_export/types/__init__.py new file mode 100644 index 00000000..f9ec9717 --- /dev/null +++ b/src/intercom/unstable/data_export/types/__init__.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .data_export import DataExport + from .data_export_status import DataExportStatus +_dynamic_imports: typing.Dict[str, str] = {"DataExport": ".data_export", "DataExportStatus": ".data_export_status"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["DataExport", "DataExportStatus"] diff --git a/src/intercom/unstable/data_export/types/data_export.py b/src/intercom/unstable/data_export/types/data_export.py new file mode 100644 index 00000000..e6d28d1a --- /dev/null +++ b/src/intercom/unstable/data_export/types/data_export.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .data_export_status import DataExportStatus + + +class DataExport(UncheckedBaseModel): + """ + The data export api is used to view all message sent & viewed in a given timeframe. + """ + + job_identfier: typing.Optional[str] = pydantic.Field(default=None) + """ + The identifier for your job. + """ + + status: typing.Optional[DataExportStatus] = pydantic.Field(default=None) + """ + The current state of your job. + """ + + download_expires_at: typing.Optional[str] = pydantic.Field(default=None) + """ + The time after which you will not be able to access the data. + """ + + download_url: typing.Optional[str] = pydantic.Field(default=None) + """ + The location where you can download your data. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/data_export/types/data_export_status.py b/src/intercom/unstable/data_export/types/data_export_status.py new file mode 100644 index 00000000..04cd48c9 --- /dev/null +++ b/src/intercom/unstable/data_export/types/data_export_status.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +DataExportStatus = typing.Union[ + typing.Literal["pending", "in_progress", "failed", "completed", "no_data", "canceled"], typing.Any +] diff --git a/src/intercom/unstable/emails/__init__.py b/src/intercom/unstable/emails/__init__.py new file mode 100644 index 00000000..4fc96499 --- /dev/null +++ b/src/intercom/unstable/emails/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import EmailList, EmailSetting +_dynamic_imports: typing.Dict[str, str] = {"EmailList": ".types", "EmailSetting": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["EmailList", "EmailSetting"] diff --git a/src/intercom/unstable/emails/client.py b/src/intercom/unstable/emails/client.py new file mode 100644 index 00000000..cbcb564c --- /dev/null +++ b/src/intercom/unstable/emails/client.py @@ -0,0 +1,171 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from .raw_client import AsyncRawEmailsClient, RawEmailsClient +from .types.email_list import EmailList +from .types.email_setting import EmailSetting + + +class EmailsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawEmailsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawEmailsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawEmailsClient + """ + return self._raw_client + + def list_emails(self, *, request_options: typing.Optional[RequestOptions] = None) -> EmailList: + """ + Lists all sender email address settings for the workspace + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + EmailList + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.emails.list_emails() + """ + _response = self._raw_client.list_emails(request_options=request_options) + return _response.data + + def retrieve_email(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> EmailSetting: + """ + Fetches a specific email setting by its unique identifier + + Parameters + ---------- + id : str + The unique identifier of the email setting + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + EmailSetting + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.emails.retrieve_email( + id="id", + ) + """ + _response = self._raw_client.retrieve_email(id, request_options=request_options) + return _response.data + + +class AsyncEmailsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawEmailsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawEmailsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawEmailsClient + """ + return self._raw_client + + async def list_emails(self, *, request_options: typing.Optional[RequestOptions] = None) -> EmailList: + """ + Lists all sender email address settings for the workspace + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + EmailList + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.emails.list_emails() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_emails(request_options=request_options) + return _response.data + + async def retrieve_email(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> EmailSetting: + """ + Fetches a specific email setting by its unique identifier + + Parameters + ---------- + id : str + The unique identifier of the email setting + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + EmailSetting + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.emails.retrieve_email( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.retrieve_email(id, request_options=request_options) + return _response.data diff --git a/src/intercom/unstable/emails/raw_client.py b/src/intercom/unstable/emails/raw_client.py new file mode 100644 index 00000000..28c4c01e --- /dev/null +++ b/src/intercom/unstable/emails/raw_client.py @@ -0,0 +1,240 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from .types.email_list import EmailList +from .types.email_setting import EmailSetting + + +class RawEmailsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_emails(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[EmailList]: + """ + Lists all sender email address settings for the workspace + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[EmailList] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "emails", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + EmailList, + construct_type( + type_=EmailList, # 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 retrieve_email( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[EmailSetting]: + """ + Fetches a specific email setting by its unique identifier + + Parameters + ---------- + id : str + The unique identifier of the email setting + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[EmailSetting] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + f"emails/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + EmailSetting, + construct_type( + type_=EmailSetting, # 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) + + +class AsyncRawEmailsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_emails( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[EmailList]: + """ + Lists all sender email address settings for the workspace + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[EmailList] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "emails", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + EmailList, + construct_type( + type_=EmailList, # 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 retrieve_email( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[EmailSetting]: + """ + Fetches a specific email setting by its unique identifier + + Parameters + ---------- + id : str + The unique identifier of the email setting + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[EmailSetting] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + f"emails/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + EmailSetting, + construct_type( + type_=EmailSetting, # 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) diff --git a/src/intercom/unstable/emails/types/__init__.py b/src/intercom/unstable/emails/types/__init__.py new file mode 100644 index 00000000..43134658 --- /dev/null +++ b/src/intercom/unstable/emails/types/__init__.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .email_list import EmailList + from .email_setting import EmailSetting +_dynamic_imports: typing.Dict[str, str] = {"EmailList": ".email_list", "EmailSetting": ".email_setting"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["EmailList", "EmailSetting"] diff --git a/src/intercom/unstable/emails/types/email_list.py b/src/intercom/unstable/emails/types/email_list.py new file mode 100644 index 00000000..1547f3f8 --- /dev/null +++ b/src/intercom/unstable/emails/types/email_list.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .email_setting import EmailSetting + + +class EmailList(UncheckedBaseModel): + """ + A list of email settings + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of object + """ + + data: typing.Optional[typing.List[EmailSetting]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/emails/types/email_setting.py b/src/intercom/unstable/emails/types/email_setting.py new file mode 100644 index 00000000..09c11dbe --- /dev/null +++ b/src/intercom/unstable/emails/types/email_setting.py @@ -0,0 +1,72 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class EmailSetting(UncheckedBaseModel): + """ + Represents a sender email address configuration + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of object + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + Unique email setting identifier + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + Full sender email address + """ + + verified: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the email address has been verified + """ + + domain: typing.Optional[str] = pydantic.Field(default=None) + """ + Domain portion of the email address + """ + + brand_id: typing.Optional[str] = pydantic.Field(default=None) + """ + Associated brand identifier + """ + + forwarding_enabled: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether email forwarding is active + """ + + forwarded_email_last_received_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Unix timestamp of last forwarded email received (null if never) + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Unix timestamp of creation + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Unix timestamp of last modification + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/errors/__init__.py b/src/intercom/unstable/errors/__init__.py new file mode 100644 index 00000000..c59874ab --- /dev/null +++ b/src/intercom/unstable/errors/__init__.py @@ -0,0 +1,59 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .bad_request_error import BadRequestError + from .conflict_error import ConflictError + from .forbidden_error import ForbiddenError + from .internal_server_error import InternalServerError + from .not_found_error import NotFoundError + from .too_many_requests_error import TooManyRequestsError + from .unauthorized_error import UnauthorizedError + from .unprocessable_entity_error import UnprocessableEntityError +_dynamic_imports: typing.Dict[str, str] = { + "BadRequestError": ".bad_request_error", + "ConflictError": ".conflict_error", + "ForbiddenError": ".forbidden_error", + "InternalServerError": ".internal_server_error", + "NotFoundError": ".not_found_error", + "TooManyRequestsError": ".too_many_requests_error", + "UnauthorizedError": ".unauthorized_error", + "UnprocessableEntityError": ".unprocessable_entity_error", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "BadRequestError", + "ConflictError", + "ForbiddenError", + "InternalServerError", + "NotFoundError", + "TooManyRequestsError", + "UnauthorizedError", + "UnprocessableEntityError", +] diff --git a/src/intercom/unstable/errors/bad_request_error.py b/src/intercom/unstable/errors/bad_request_error.py new file mode 100644 index 00000000..412bae78 --- /dev/null +++ b/src/intercom/unstable/errors/bad_request_error.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.api_error import ApiError + + +class BadRequestError(ApiError): + def __init__(self, body: typing.Any, headers: typing.Optional[typing.Dict[str, str]] = None): + super().__init__(status_code=400, headers=headers, body=body) diff --git a/src/intercom/unstable/errors/conflict_error.py b/src/intercom/unstable/errors/conflict_error.py new file mode 100644 index 00000000..87db744f --- /dev/null +++ b/src/intercom/unstable/errors/conflict_error.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.api_error import ApiError +from ..types.error import Error + + +class ConflictError(ApiError): + def __init__(self, body: Error, headers: typing.Optional[typing.Dict[str, str]] = None): + super().__init__(status_code=409, headers=headers, body=body) diff --git a/src/intercom/unstable/errors/forbidden_error.py b/src/intercom/unstable/errors/forbidden_error.py new file mode 100644 index 00000000..55c49da9 --- /dev/null +++ b/src/intercom/unstable/errors/forbidden_error.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.api_error import ApiError +from ..types.error import Error + + +class ForbiddenError(ApiError): + def __init__(self, body: Error, headers: typing.Optional[typing.Dict[str, str]] = None): + super().__init__(status_code=403, headers=headers, body=body) diff --git a/src/intercom/unstable/errors/internal_server_error.py b/src/intercom/unstable/errors/internal_server_error.py new file mode 100644 index 00000000..5da2523c --- /dev/null +++ b/src/intercom/unstable/errors/internal_server_error.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.api_error import ApiError +from ..types.error import Error + + +class InternalServerError(ApiError): + def __init__(self, body: Error, headers: typing.Optional[typing.Dict[str, str]] = None): + super().__init__(status_code=500, headers=headers, body=body) diff --git a/src/intercom/unstable/errors/not_found_error.py b/src/intercom/unstable/errors/not_found_error.py new file mode 100644 index 00000000..a01562a1 --- /dev/null +++ b/src/intercom/unstable/errors/not_found_error.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.api_error import ApiError + + +class NotFoundError(ApiError): + def __init__(self, body: typing.Any, headers: typing.Optional[typing.Dict[str, str]] = None): + super().__init__(status_code=404, headers=headers, body=body) diff --git a/src/intercom/unstable/errors/too_many_requests_error.py b/src/intercom/unstable/errors/too_many_requests_error.py new file mode 100644 index 00000000..e1e847a8 --- /dev/null +++ b/src/intercom/unstable/errors/too_many_requests_error.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.api_error import ApiError +from ..types.error import Error + + +class TooManyRequestsError(ApiError): + def __init__(self, body: Error, headers: typing.Optional[typing.Dict[str, str]] = None): + super().__init__(status_code=429, headers=headers, body=body) diff --git a/src/intercom/unstable/errors/unauthorized_error.py b/src/intercom/unstable/errors/unauthorized_error.py new file mode 100644 index 00000000..4e55e061 --- /dev/null +++ b/src/intercom/unstable/errors/unauthorized_error.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.api_error import ApiError +from ..types.error import Error + + +class UnauthorizedError(ApiError): + def __init__(self, body: Error, headers: typing.Optional[typing.Dict[str, str]] = None): + super().__init__(status_code=401, headers=headers, body=body) diff --git a/src/intercom/unstable/errors/unprocessable_entity_error.py b/src/intercom/unstable/errors/unprocessable_entity_error.py new file mode 100644 index 00000000..0c28643e --- /dev/null +++ b/src/intercom/unstable/errors/unprocessable_entity_error.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.api_error import ApiError + + +class UnprocessableEntityError(ApiError): + def __init__(self, body: typing.Any, headers: typing.Optional[typing.Dict[str, str]] = None): + super().__init__(status_code=422, headers=headers, body=body) diff --git a/src/intercom/unstable/export/__init__.py b/src/intercom/unstable/export/__init__.py new file mode 100644 index 00000000..f5e527fe --- /dev/null +++ b/src/intercom/unstable/export/__init__.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + GetExportReportingDataGetDatasetsResponse, + GetExportReportingDataGetDatasetsResponseDataItem, + GetExportReportingDataGetDatasetsResponseDataItemAttributesItem, + PostExportReportingDataEnqueueResponse, + ) +_dynamic_imports: typing.Dict[str, str] = { + "GetExportReportingDataGetDatasetsResponse": ".types", + "GetExportReportingDataGetDatasetsResponseDataItem": ".types", + "GetExportReportingDataGetDatasetsResponseDataItemAttributesItem": ".types", + "PostExportReportingDataEnqueueResponse": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "GetExportReportingDataGetDatasetsResponse", + "GetExportReportingDataGetDatasetsResponseDataItem", + "GetExportReportingDataGetDatasetsResponseDataItemAttributesItem", + "PostExportReportingDataEnqueueResponse", +] diff --git a/src/intercom/unstable/export/client.py b/src/intercom/unstable/export/client.py new file mode 100644 index 00000000..a3b9cc87 --- /dev/null +++ b/src/intercom/unstable/export/client.py @@ -0,0 +1,214 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from .raw_client import AsyncRawExportClient, RawExportClient +from .types.get_export_reporting_data_get_datasets_response import GetExportReportingDataGetDatasetsResponse +from .types.post_export_reporting_data_enqueue_response import PostExportReportingDataEnqueueResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class ExportClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawExportClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawExportClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawExportClient + """ + return self._raw_client + + def enqueue_a_new_reporting_data_export_job( + self, + *, + dataset_id: str, + attribute_ids: typing.Sequence[str], + start_time: int, + end_time: int, + request_options: typing.Optional[RequestOptions] = None, + ) -> PostExportReportingDataEnqueueResponse: + """ + Parameters + ---------- + dataset_id : str + + attribute_ids : typing.Sequence[str] + + start_time : int + + end_time : int + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PostExportReportingDataEnqueueResponse + Job enqueued successfully + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.export.enqueue_a_new_reporting_data_export_job( + dataset_id="conversation", + attribute_ids=["conversation_id", "conversation_started_at"], + start_time=1717490000, + end_time=1717510000, + ) + """ + _response = self._raw_client.enqueue_a_new_reporting_data_export_job( + dataset_id=dataset_id, + attribute_ids=attribute_ids, + start_time=start_time, + end_time=end_time, + request_options=request_options, + ) + return _response.data + + def list_available_datasets_and_attributes( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> GetExportReportingDataGetDatasetsResponse: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + GetExportReportingDataGetDatasetsResponse + List of datasets + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.export.list_available_datasets_and_attributes() + """ + _response = self._raw_client.list_available_datasets_and_attributes(request_options=request_options) + return _response.data + + +class AsyncExportClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawExportClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawExportClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawExportClient + """ + return self._raw_client + + async def enqueue_a_new_reporting_data_export_job( + self, + *, + dataset_id: str, + attribute_ids: typing.Sequence[str], + start_time: int, + end_time: int, + request_options: typing.Optional[RequestOptions] = None, + ) -> PostExportReportingDataEnqueueResponse: + """ + Parameters + ---------- + dataset_id : str + + attribute_ids : typing.Sequence[str] + + start_time : int + + end_time : int + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PostExportReportingDataEnqueueResponse + Job enqueued successfully + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.export.enqueue_a_new_reporting_data_export_job( + dataset_id="conversation", + attribute_ids=["conversation_id", "conversation_started_at"], + start_time=1717490000, + end_time=1717510000, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.enqueue_a_new_reporting_data_export_job( + dataset_id=dataset_id, + attribute_ids=attribute_ids, + start_time=start_time, + end_time=end_time, + request_options=request_options, + ) + return _response.data + + async def list_available_datasets_and_attributes( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> GetExportReportingDataGetDatasetsResponse: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + GetExportReportingDataGetDatasetsResponse + List of datasets + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.export.list_available_datasets_and_attributes() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_available_datasets_and_attributes(request_options=request_options) + return _response.data diff --git a/src/intercom/unstable/export/raw_client.py b/src/intercom/unstable/export/raw_client.py new file mode 100644 index 00000000..f1b23339 --- /dev/null +++ b/src/intercom/unstable/export/raw_client.py @@ -0,0 +1,279 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.http_response import AsyncHttpResponse, HttpResponse +from ...core.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.bad_request_error import BadRequestError +from ..errors.too_many_requests_error import TooManyRequestsError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from .types.get_export_reporting_data_get_datasets_response import GetExportReportingDataGetDatasetsResponse +from .types.post_export_reporting_data_enqueue_response import PostExportReportingDataEnqueueResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawExportClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def enqueue_a_new_reporting_data_export_job( + self, + *, + dataset_id: str, + attribute_ids: typing.Sequence[str], + start_time: int, + end_time: int, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[PostExportReportingDataEnqueueResponse]: + """ + Parameters + ---------- + dataset_id : str + + attribute_ids : typing.Sequence[str] + + start_time : int + + end_time : int + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[PostExportReportingDataEnqueueResponse] + Job enqueued successfully + """ + _response = self._client_wrapper.httpx_client.request( + "export/reporting_data/enqueue", + method="POST", + json={ + "dataset_id": dataset_id, + "attribute_ids": attribute_ids, + "start_time": start_time, + "end_time": end_time, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PostExportReportingDataEnqueueResponse, + construct_type( + type_=PostExportReportingDataEnqueueResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 == 429: + raise TooManyRequestsError( + 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_available_datasets_and_attributes( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[GetExportReportingDataGetDatasetsResponse]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[GetExportReportingDataGetDatasetsResponse] + List of datasets + """ + _response = self._client_wrapper.httpx_client.request( + "export/reporting_data/get_datasets", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + GetExportReportingDataGetDatasetsResponse, + construct_type( + type_=GetExportReportingDataGetDatasetsResponse, # 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 AsyncRawExportClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def enqueue_a_new_reporting_data_export_job( + self, + *, + dataset_id: str, + attribute_ids: typing.Sequence[str], + start_time: int, + end_time: int, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[PostExportReportingDataEnqueueResponse]: + """ + Parameters + ---------- + dataset_id : str + + attribute_ids : typing.Sequence[str] + + start_time : int + + end_time : int + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[PostExportReportingDataEnqueueResponse] + Job enqueued successfully + """ + _response = await self._client_wrapper.httpx_client.request( + "export/reporting_data/enqueue", + method="POST", + json={ + "dataset_id": dataset_id, + "attribute_ids": attribute_ids, + "start_time": start_time, + "end_time": end_time, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PostExportReportingDataEnqueueResponse, + construct_type( + type_=PostExportReportingDataEnqueueResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 == 429: + raise TooManyRequestsError( + 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_available_datasets_and_attributes( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[GetExportReportingDataGetDatasetsResponse]: + """ + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[GetExportReportingDataGetDatasetsResponse] + List of datasets + """ + _response = await self._client_wrapper.httpx_client.request( + "export/reporting_data/get_datasets", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + GetExportReportingDataGetDatasetsResponse, + construct_type( + type_=GetExportReportingDataGetDatasetsResponse, # 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) diff --git a/src/intercom/unstable/export/types/__init__.py b/src/intercom/unstable/export/types/__init__.py new file mode 100644 index 00000000..44cfc00c --- /dev/null +++ b/src/intercom/unstable/export/types/__init__.py @@ -0,0 +1,51 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .get_export_reporting_data_get_datasets_response import GetExportReportingDataGetDatasetsResponse + from .get_export_reporting_data_get_datasets_response_data_item import ( + GetExportReportingDataGetDatasetsResponseDataItem, + ) + from .get_export_reporting_data_get_datasets_response_data_item_attributes_item import ( + GetExportReportingDataGetDatasetsResponseDataItemAttributesItem, + ) + from .post_export_reporting_data_enqueue_response import PostExportReportingDataEnqueueResponse +_dynamic_imports: typing.Dict[str, str] = { + "GetExportReportingDataGetDatasetsResponse": ".get_export_reporting_data_get_datasets_response", + "GetExportReportingDataGetDatasetsResponseDataItem": ".get_export_reporting_data_get_datasets_response_data_item", + "GetExportReportingDataGetDatasetsResponseDataItemAttributesItem": ".get_export_reporting_data_get_datasets_response_data_item_attributes_item", + "PostExportReportingDataEnqueueResponse": ".post_export_reporting_data_enqueue_response", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "GetExportReportingDataGetDatasetsResponse", + "GetExportReportingDataGetDatasetsResponseDataItem", + "GetExportReportingDataGetDatasetsResponseDataItemAttributesItem", + "PostExportReportingDataEnqueueResponse", +] diff --git a/src/intercom/unstable/export/types/get_export_reporting_data_get_datasets_response.py b/src/intercom/unstable/export/types/get_export_reporting_data_get_datasets_response.py new file mode 100644 index 00000000..1c54856d --- /dev/null +++ b/src/intercom/unstable/export/types/get_export_reporting_data_get_datasets_response.py @@ -0,0 +1,22 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .get_export_reporting_data_get_datasets_response_data_item import GetExportReportingDataGetDatasetsResponseDataItem + + +class GetExportReportingDataGetDatasetsResponse(UncheckedBaseModel): + type: typing.Optional[str] = None + data: typing.Optional[typing.List[GetExportReportingDataGetDatasetsResponseDataItem]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/export/types/get_export_reporting_data_get_datasets_response_data_item.py b/src/intercom/unstable/export/types/get_export_reporting_data_get_datasets_response_data_item.py new file mode 100644 index 00000000..2fc5ee0d --- /dev/null +++ b/src/intercom/unstable/export/types/get_export_reporting_data_get_datasets_response_data_item.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .get_export_reporting_data_get_datasets_response_data_item_attributes_item import ( + GetExportReportingDataGetDatasetsResponseDataItemAttributesItem, +) + + +class GetExportReportingDataGetDatasetsResponseDataItem(UncheckedBaseModel): + id: typing.Optional[str] = None + name: typing.Optional[str] = None + description: typing.Optional[str] = None + default_time_attribute_id: typing.Optional[str] = None + attributes: typing.Optional[typing.List[GetExportReportingDataGetDatasetsResponseDataItemAttributesItem]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/export/types/get_export_reporting_data_get_datasets_response_data_item_attributes_item.py b/src/intercom/unstable/export/types/get_export_reporting_data_get_datasets_response_data_item_attributes_item.py new file mode 100644 index 00000000..bd6862ae --- /dev/null +++ b/src/intercom/unstable/export/types/get_export_reporting_data_get_datasets_response_data_item_attributes_item.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class GetExportReportingDataGetDatasetsResponseDataItemAttributesItem(UncheckedBaseModel): + id: typing.Optional[str] = None + name: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/export/types/post_export_reporting_data_enqueue_response.py b/src/intercom/unstable/export/types/post_export_reporting_data_enqueue_response.py new file mode 100644 index 00000000..e3edcf49 --- /dev/null +++ b/src/intercom/unstable/export/types/post_export_reporting_data_enqueue_response.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class PostExportReportingDataEnqueueResponse(UncheckedBaseModel): + job_identifier: typing.Optional[str] = None + status: typing.Optional[str] = None + download_url: typing.Optional[str] = None + download_expires_at: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/help_center/__init__.py b/src/intercom/unstable/help_center/__init__.py new file mode 100644 index 00000000..4100fda2 --- /dev/null +++ b/src/intercom/unstable/help_center/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import Collection, HelpCenter, HelpCenterList +_dynamic_imports: typing.Dict[str, str] = {"Collection": ".types", "HelpCenter": ".types", "HelpCenterList": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Collection", "HelpCenter", "HelpCenterList"] diff --git a/src/intercom/unstable/help_center/client.py b/src/intercom/unstable/help_center/client.py new file mode 100644 index 00000000..c2535e9f --- /dev/null +++ b/src/intercom/unstable/help_center/client.py @@ -0,0 +1,640 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..types.collection_list import CollectionList +from ..types.deleted_collection_object import DeletedCollectionObject +from ..types.group_translated_content import GroupTranslatedContent +from .raw_client import AsyncRawHelpCenterClient, RawHelpCenterClient +from .types.collection import Collection +from .types.help_center import HelpCenter +from .types.help_center_list import HelpCenterList + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class HelpCenterClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawHelpCenterClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawHelpCenterClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawHelpCenterClient + """ + return self._raw_client + + def list_all_collections(self, *, request_options: typing.Optional[RequestOptions] = None) -> CollectionList: + """ + You can fetch a list of all collections by making a GET request to `https://api.intercom.io/help_center/collections`. + + Collections will be returned in descending order on the `updated_at` attribute. This means if you need to iterate through results then we'll show the most recently updated collections first. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CollectionList + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.help_center.list_all_collections() + """ + _response = self._raw_client.list_all_collections(request_options=request_options) + return _response.data + + def create_collection( + self, + *, + name: str, + description: typing.Optional[str] = OMIT, + translated_content: typing.Optional[GroupTranslatedContent] = OMIT, + parent_id: typing.Optional[str] = OMIT, + help_center_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Collection: + """ + You can create a new collection by making a POST request to `https://api.intercom.io/help_center/collections.` + + Parameters + ---------- + name : str + The name of the collection. For multilingual collections, this will be the name of the default language's content. + + description : typing.Optional[str] + The description of the collection. For multilingual collections, this will be the description of the default language's content. + + translated_content : typing.Optional[GroupTranslatedContent] + + parent_id : typing.Optional[str] + The id of the parent collection. If `null` then it will be created as the first level collection. + + help_center_id : typing.Optional[int] + The id of the help center where the collection will be created. If `null` then it will be created in the default help center. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Collection + collection created + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.help_center.create_collection( + name="Thanks for everything", + ) + """ + _response = self._raw_client.create_collection( + name=name, + description=description, + translated_content=translated_content, + parent_id=parent_id, + help_center_id=help_center_id, + request_options=request_options, + ) + return _response.data + + def retrieve_collection(self, id: int, *, request_options: typing.Optional[RequestOptions] = None) -> Collection: + """ + You can fetch the details of a single collection by making a GET request to `https://api.intercom.io/help_center/collections/`. + + Parameters + ---------- + id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Collection + Collection found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.help_center.retrieve_collection( + id=1, + ) + """ + _response = self._raw_client.retrieve_collection(id, request_options=request_options) + return _response.data + + def update_collection( + self, + id: int, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + translated_content: typing.Optional[GroupTranslatedContent] = OMIT, + parent_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Collection: + """ + You can update the details of a single collection by making a PUT request to `https://api.intercom.io/collections/`. + + Parameters + ---------- + id : int + The unique identifier for the collection which is given by Intercom. + + name : typing.Optional[str] + The name of the collection. For multilingual collections, this will be the name of the default language's content. + + description : typing.Optional[str] + The description of the collection. For multilingual collections, this will be the description of the default language's content. + + translated_content : typing.Optional[GroupTranslatedContent] + + parent_id : typing.Optional[str] + The id of the parent collection. If `null` then it will be updated as the first level collection. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Collection + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.help_center.update_collection( + id=1, + name="Update collection name", + ) + """ + _response = self._raw_client.update_collection( + id, + name=name, + description=description, + translated_content=translated_content, + parent_id=parent_id, + request_options=request_options, + ) + return _response.data + + def delete_collection( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeletedCollectionObject: + """ + You can delete a single collection by making a DELETE request to `https://api.intercom.io/collections/`. + + Parameters + ---------- + id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeletedCollectionObject + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.help_center.delete_collection( + id=1, + ) + """ + _response = self._raw_client.delete_collection(id, request_options=request_options) + return _response.data + + def retrieve_help_center(self, id: int, *, request_options: typing.Optional[RequestOptions] = None) -> HelpCenter: + """ + You can fetch the details of a single Help Center by making a GET request to `https://api.intercom.io/help_center/help_center/`. + + Parameters + ---------- + id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HelpCenter + Collection found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.help_center.retrieve_help_center( + id=1, + ) + """ + _response = self._raw_client.retrieve_help_center(id, request_options=request_options) + return _response.data + + def list_help_centers(self, *, request_options: typing.Optional[RequestOptions] = None) -> HelpCenterList: + """ + You can list all Help Centers by making a GET request to `https://api.intercom.io/help_center/help_centers`. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HelpCenterList + Help Centers found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.help_center.list_help_centers() + """ + _response = self._raw_client.list_help_centers(request_options=request_options) + return _response.data + + +class AsyncHelpCenterClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawHelpCenterClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawHelpCenterClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawHelpCenterClient + """ + return self._raw_client + + async def list_all_collections(self, *, request_options: typing.Optional[RequestOptions] = None) -> CollectionList: + """ + You can fetch a list of all collections by making a GET request to `https://api.intercom.io/help_center/collections`. + + Collections will be returned in descending order on the `updated_at` attribute. This means if you need to iterate through results then we'll show the most recently updated collections first. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CollectionList + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.help_center.list_all_collections() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_all_collections(request_options=request_options) + return _response.data + + async def create_collection( + self, + *, + name: str, + description: typing.Optional[str] = OMIT, + translated_content: typing.Optional[GroupTranslatedContent] = OMIT, + parent_id: typing.Optional[str] = OMIT, + help_center_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Collection: + """ + You can create a new collection by making a POST request to `https://api.intercom.io/help_center/collections.` + + Parameters + ---------- + name : str + The name of the collection. For multilingual collections, this will be the name of the default language's content. + + description : typing.Optional[str] + The description of the collection. For multilingual collections, this will be the description of the default language's content. + + translated_content : typing.Optional[GroupTranslatedContent] + + parent_id : typing.Optional[str] + The id of the parent collection. If `null` then it will be created as the first level collection. + + help_center_id : typing.Optional[int] + The id of the help center where the collection will be created. If `null` then it will be created in the default help center. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Collection + collection created + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.help_center.create_collection( + name="Thanks for everything", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_collection( + name=name, + description=description, + translated_content=translated_content, + parent_id=parent_id, + help_center_id=help_center_id, + request_options=request_options, + ) + return _response.data + + async def retrieve_collection( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> Collection: + """ + You can fetch the details of a single collection by making a GET request to `https://api.intercom.io/help_center/collections/`. + + Parameters + ---------- + id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Collection + Collection found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.help_center.retrieve_collection( + id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.retrieve_collection(id, request_options=request_options) + return _response.data + + async def update_collection( + self, + id: int, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + translated_content: typing.Optional[GroupTranslatedContent] = OMIT, + parent_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Collection: + """ + You can update the details of a single collection by making a PUT request to `https://api.intercom.io/collections/`. + + Parameters + ---------- + id : int + The unique identifier for the collection which is given by Intercom. + + name : typing.Optional[str] + The name of the collection. For multilingual collections, this will be the name of the default language's content. + + description : typing.Optional[str] + The description of the collection. For multilingual collections, this will be the description of the default language's content. + + translated_content : typing.Optional[GroupTranslatedContent] + + parent_id : typing.Optional[str] + The id of the parent collection. If `null` then it will be updated as the first level collection. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Collection + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.help_center.update_collection( + id=1, + name="Update collection name", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update_collection( + id, + name=name, + description=description, + translated_content=translated_content, + parent_id=parent_id, + request_options=request_options, + ) + return _response.data + + async def delete_collection( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeletedCollectionObject: + """ + You can delete a single collection by making a DELETE request to `https://api.intercom.io/collections/`. + + Parameters + ---------- + id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeletedCollectionObject + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.help_center.delete_collection( + id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_collection(id, request_options=request_options) + return _response.data + + async def retrieve_help_center( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HelpCenter: + """ + You can fetch the details of a single Help Center by making a GET request to `https://api.intercom.io/help_center/help_center/`. + + Parameters + ---------- + id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HelpCenter + Collection found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.help_center.retrieve_help_center( + id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.retrieve_help_center(id, request_options=request_options) + return _response.data + + async def list_help_centers(self, *, request_options: typing.Optional[RequestOptions] = None) -> HelpCenterList: + """ + You can list all Help Centers by making a GET request to `https://api.intercom.io/help_center/help_centers`. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HelpCenterList + Help Centers found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.help_center.list_help_centers() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_help_centers(request_options=request_options) + return _response.data diff --git a/src/intercom/unstable/help_center/raw_client.py b/src/intercom/unstable/help_center/raw_client.py new file mode 100644 index 00000000..95f06198 --- /dev/null +++ b/src/intercom/unstable/help_center/raw_client.py @@ -0,0 +1,959 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.serialization import convert_and_respect_annotation_metadata +from ...core.unchecked_base_model import construct_type +from ..errors.bad_request_error import BadRequestError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.collection_list import CollectionList +from ..types.deleted_collection_object import DeletedCollectionObject +from ..types.error import Error +from ..types.group_translated_content import GroupTranslatedContent +from .types.collection import Collection +from .types.help_center import HelpCenter +from .types.help_center_list import HelpCenterList + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawHelpCenterClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_all_collections( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[CollectionList]: + """ + You can fetch a list of all collections by making a GET request to `https://api.intercom.io/help_center/collections`. + + Collections will be returned in descending order on the `updated_at` attribute. This means if you need to iterate through results then we'll show the most recently updated collections first. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CollectionList] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + "help_center/collections", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CollectionList, + construct_type( + type_=CollectionList, # 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 create_collection( + self, + *, + name: str, + description: typing.Optional[str] = OMIT, + translated_content: typing.Optional[GroupTranslatedContent] = OMIT, + parent_id: typing.Optional[str] = OMIT, + help_center_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Collection]: + """ + You can create a new collection by making a POST request to `https://api.intercom.io/help_center/collections.` + + Parameters + ---------- + name : str + The name of the collection. For multilingual collections, this will be the name of the default language's content. + + description : typing.Optional[str] + The description of the collection. For multilingual collections, this will be the description of the default language's content. + + translated_content : typing.Optional[GroupTranslatedContent] + + parent_id : typing.Optional[str] + The id of the parent collection. If `null` then it will be created as the first level collection. + + help_center_id : typing.Optional[int] + The id of the help center where the collection will be created. If `null` then it will be created in the default help center. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Collection] + collection created + """ + _response = self._client_wrapper.httpx_client.request( + "help_center/collections", + method="POST", + json={ + "name": name, + "description": description, + "translated_content": convert_and_respect_annotation_metadata( + object_=translated_content, annotation=GroupTranslatedContent, direction="write" + ), + "parent_id": parent_id, + "help_center_id": help_center_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Collection, + construct_type( + type_=Collection, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 retrieve_collection( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Collection]: + """ + You can fetch the details of a single collection by making a GET request to `https://api.intercom.io/help_center/collections/`. + + Parameters + ---------- + id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Collection] + Collection found + """ + _response = self._client_wrapper.httpx_client.request( + f"help_center/collections/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Collection, + construct_type( + type_=Collection, # 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 update_collection( + self, + id: int, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + translated_content: typing.Optional[GroupTranslatedContent] = OMIT, + parent_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Collection]: + """ + You can update the details of a single collection by making a PUT request to `https://api.intercom.io/collections/`. + + Parameters + ---------- + id : int + The unique identifier for the collection which is given by Intercom. + + name : typing.Optional[str] + The name of the collection. For multilingual collections, this will be the name of the default language's content. + + description : typing.Optional[str] + The description of the collection. For multilingual collections, this will be the description of the default language's content. + + translated_content : typing.Optional[GroupTranslatedContent] + + parent_id : typing.Optional[str] + The id of the parent collection. If `null` then it will be updated as the first level collection. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Collection] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"help_center/collections/{jsonable_encoder(id)}", + method="PUT", + json={ + "name": name, + "description": description, + "translated_content": convert_and_respect_annotation_metadata( + object_=translated_content, annotation=GroupTranslatedContent, direction="write" + ), + "parent_id": parent_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Collection, + construct_type( + type_=Collection, # 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 delete_collection( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DeletedCollectionObject]: + """ + You can delete a single collection by making a DELETE request to `https://api.intercom.io/collections/`. + + Parameters + ---------- + id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeletedCollectionObject] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"help_center/collections/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeletedCollectionObject, + construct_type( + type_=DeletedCollectionObject, # 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 retrieve_help_center( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[HelpCenter]: + """ + You can fetch the details of a single Help Center by making a GET request to `https://api.intercom.io/help_center/help_center/`. + + Parameters + ---------- + id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[HelpCenter] + Collection found + """ + _response = self._client_wrapper.httpx_client.request( + f"help_center/help_centers/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + HelpCenter, + construct_type( + type_=HelpCenter, # 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_help_centers( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[HelpCenterList]: + """ + You can list all Help Centers by making a GET request to `https://api.intercom.io/help_center/help_centers`. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[HelpCenterList] + Help Centers found + """ + _response = self._client_wrapper.httpx_client.request( + "help_center/help_centers", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + HelpCenterList, + construct_type( + type_=HelpCenterList, # 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) + + +class AsyncRawHelpCenterClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_all_collections( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[CollectionList]: + """ + You can fetch a list of all collections by making a GET request to `https://api.intercom.io/help_center/collections`. + + Collections will be returned in descending order on the `updated_at` attribute. This means if you need to iterate through results then we'll show the most recently updated collections first. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CollectionList] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + "help_center/collections", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CollectionList, + construct_type( + type_=CollectionList, # 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 create_collection( + self, + *, + name: str, + description: typing.Optional[str] = OMIT, + translated_content: typing.Optional[GroupTranslatedContent] = OMIT, + parent_id: typing.Optional[str] = OMIT, + help_center_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Collection]: + """ + You can create a new collection by making a POST request to `https://api.intercom.io/help_center/collections.` + + Parameters + ---------- + name : str + The name of the collection. For multilingual collections, this will be the name of the default language's content. + + description : typing.Optional[str] + The description of the collection. For multilingual collections, this will be the description of the default language's content. + + translated_content : typing.Optional[GroupTranslatedContent] + + parent_id : typing.Optional[str] + The id of the parent collection. If `null` then it will be created as the first level collection. + + help_center_id : typing.Optional[int] + The id of the help center where the collection will be created. If `null` then it will be created in the default help center. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Collection] + collection created + """ + _response = await self._client_wrapper.httpx_client.request( + "help_center/collections", + method="POST", + json={ + "name": name, + "description": description, + "translated_content": convert_and_respect_annotation_metadata( + object_=translated_content, annotation=GroupTranslatedContent, direction="write" + ), + "parent_id": parent_id, + "help_center_id": help_center_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Collection, + construct_type( + type_=Collection, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 retrieve_collection( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Collection]: + """ + You can fetch the details of a single collection by making a GET request to `https://api.intercom.io/help_center/collections/`. + + Parameters + ---------- + id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Collection] + Collection found + """ + _response = await self._client_wrapper.httpx_client.request( + f"help_center/collections/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Collection, + construct_type( + type_=Collection, # 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 update_collection( + self, + id: int, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + translated_content: typing.Optional[GroupTranslatedContent] = OMIT, + parent_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Collection]: + """ + You can update the details of a single collection by making a PUT request to `https://api.intercom.io/collections/`. + + Parameters + ---------- + id : int + The unique identifier for the collection which is given by Intercom. + + name : typing.Optional[str] + The name of the collection. For multilingual collections, this will be the name of the default language's content. + + description : typing.Optional[str] + The description of the collection. For multilingual collections, this will be the description of the default language's content. + + translated_content : typing.Optional[GroupTranslatedContent] + + parent_id : typing.Optional[str] + The id of the parent collection. If `null` then it will be updated as the first level collection. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Collection] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"help_center/collections/{jsonable_encoder(id)}", + method="PUT", + json={ + "name": name, + "description": description, + "translated_content": convert_and_respect_annotation_metadata( + object_=translated_content, annotation=GroupTranslatedContent, direction="write" + ), + "parent_id": parent_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Collection, + construct_type( + type_=Collection, # 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 delete_collection( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DeletedCollectionObject]: + """ + You can delete a single collection by making a DELETE request to `https://api.intercom.io/collections/`. + + Parameters + ---------- + id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeletedCollectionObject] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"help_center/collections/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeletedCollectionObject, + construct_type( + type_=DeletedCollectionObject, # 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 retrieve_help_center( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[HelpCenter]: + """ + You can fetch the details of a single Help Center by making a GET request to `https://api.intercom.io/help_center/help_center/`. + + Parameters + ---------- + id : int + The unique identifier for the collection which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[HelpCenter] + Collection found + """ + _response = await self._client_wrapper.httpx_client.request( + f"help_center/help_centers/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + HelpCenter, + construct_type( + type_=HelpCenter, # 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_help_centers( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[HelpCenterList]: + """ + You can list all Help Centers by making a GET request to `https://api.intercom.io/help_center/help_centers`. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[HelpCenterList] + Help Centers found + """ + _response = await self._client_wrapper.httpx_client.request( + "help_center/help_centers", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + HelpCenterList, + construct_type( + type_=HelpCenterList, # 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) diff --git a/src/intercom/unstable/help_center/types/__init__.py b/src/intercom/unstable/help_center/types/__init__.py new file mode 100644 index 00000000..57d6f641 --- /dev/null +++ b/src/intercom/unstable/help_center/types/__init__.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .collection import Collection + from .help_center import HelpCenter + from .help_center_list import HelpCenterList +_dynamic_imports: typing.Dict[str, str] = { + "Collection": ".collection", + "HelpCenter": ".help_center", + "HelpCenterList": ".help_center_list", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Collection", "HelpCenter", "HelpCenterList"] diff --git a/src/intercom/unstable/help_center/types/collection.py b/src/intercom/unstable/help_center/types/collection.py new file mode 100644 index 00000000..efe080f8 --- /dev/null +++ b/src/intercom/unstable/help_center/types/collection.py @@ -0,0 +1,84 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...types.group_translated_content import GroupTranslatedContent + + +class Collection(UncheckedBaseModel): + """ + Collections are top level containers for Articles within the Help Center. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the collection which is given by Intercom. + """ + + workspace_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the workspace which the collection belongs to. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the collection. For multilingual collections, this will be the name of the default language's content. + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + The description of the collection. For multilingual help centers, this will be the description of the collection for the default language. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the article was created (seconds). For multilingual articles, this will be the timestamp of creation of the default language's content. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the article was last updated (seconds). For multilingual articles, this will be the timestamp of last update of the default language's content. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL of the collection. For multilingual help centers, this will be the URL of the collection for the default language. + """ + + icon: typing.Optional[str] = pydantic.Field(default=None) + """ + The icon of the collection. + """ + + order: typing.Optional[int] = pydantic.Field(default=None) + """ + The order of the section in relation to others sections within a collection. Values go from `0` upwards. `0` is the default if there's no order. + """ + + default_locale: typing.Optional[str] = pydantic.Field(default=None) + """ + The default locale of the help center. This field is only returned for multilingual help centers. + """ + + translated_content: typing.Optional[GroupTranslatedContent] = None + parent_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the parent collection. If `null` then it is the first level collection. + """ + + help_center_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of the help center the collection is in. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/help_center/types/help_center.py b/src/intercom/unstable/help_center/types/help_center.py new file mode 100644 index 00000000..93df1fb2 --- /dev/null +++ b/src/intercom/unstable/help_center/types/help_center.py @@ -0,0 +1,67 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class HelpCenter(UncheckedBaseModel): + """ + Help Centers contain collections + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the Help Center which is given by Intercom. + """ + + workspace_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the workspace which the Help Center belongs to. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the Help Center was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the Help Center was last updated. + """ + + identifier: typing.Optional[str] = pydantic.Field(default=None) + """ + The identifier of the Help Center. This is used in the URL of the Help Center. + """ + + website_turned_on: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the Help Center is turned on or not. This is controlled in your Help Center settings. + """ + + display_name: typing.Optional[str] = pydantic.Field(default=None) + """ + The display name of the Help Center only seen by teammates. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL for the help center, if you have a custom domain then this will show the URL using the custom domain. + """ + + custom_domain: typing.Optional[str] = pydantic.Field(default=None) + """ + Custom domain configured for the help center + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/help_center/types/help_center_list.py b/src/intercom/unstable/help_center/types/help_center_list.py new file mode 100644 index 00000000..ec82bff3 --- /dev/null +++ b/src/intercom/unstable/help_center/types/help_center_list.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .help_center import HelpCenter + + +class HelpCenterList(UncheckedBaseModel): + """ + A list of Help Centers belonging to the App + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object - `list`. + """ + + data: typing.Optional[typing.List[HelpCenter]] = pydantic.Field(default=None) + """ + An array of Help Center objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/internal_articles/__init__.py b/src/intercom/unstable/internal_articles/__init__.py new file mode 100644 index 00000000..f7b287f2 --- /dev/null +++ b/src/intercom/unstable/internal_articles/__init__.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import InternalArticleListItem, InternalArticleSearchResponse, InternalArticleSearchResponseData +_dynamic_imports: typing.Dict[str, str] = { + "InternalArticleListItem": ".types", + "InternalArticleSearchResponse": ".types", + "InternalArticleSearchResponseData": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["InternalArticleListItem", "InternalArticleSearchResponse", "InternalArticleSearchResponseData"] diff --git a/src/intercom/unstable/internal_articles/client.py b/src/intercom/unstable/internal_articles/client.py new file mode 100644 index 00000000..6872e1fa --- /dev/null +++ b/src/intercom/unstable/internal_articles/client.py @@ -0,0 +1,543 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..articles.types.internal_article import InternalArticle +from ..types.create_internal_article_request import CreateInternalArticleRequest +from ..types.deleted_internal_article_object import DeletedInternalArticleObject +from ..types.internal_article_list import InternalArticleList +from .raw_client import AsyncRawInternalArticlesClient, RawInternalArticlesClient +from .types.internal_article_search_response import InternalArticleSearchResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class InternalArticlesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawInternalArticlesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawInternalArticlesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawInternalArticlesClient + """ + return self._raw_client + + def list_internal_articles(self, *, request_options: typing.Optional[RequestOptions] = None) -> InternalArticleList: + """ + You can fetch a list of all internal articles by making a GET request to `https://api.intercom.io/internal_articles`. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + InternalArticleList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.internal_articles.list_internal_articles() + """ + _response = self._raw_client.list_internal_articles(request_options=request_options) + return _response.data + + def create_internal_article( + self, + *, + request: typing.Optional[CreateInternalArticleRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> InternalArticle: + """ + You can create a new internal article by making a POST request to `https://api.intercom.io/internal_articles`. + + Parameters + ---------- + request : typing.Optional[CreateInternalArticleRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + InternalArticle + internal article created + + Examples + -------- + from intercom import Intercom + from intercom.unstable import CreateInternalArticleRequest + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.internal_articles.create_internal_article( + request=CreateInternalArticleRequest( + title="Thanks for everything", + body="Body of the Article", + author_id=991266252, + owner_id=991266252, + ), + ) + """ + _response = self._raw_client.create_internal_article(request=request, request_options=request_options) + return _response.data + + def retrieve_internal_article( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> InternalArticle: + """ + You can fetch the details of a single internal article by making a GET request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + InternalArticle + Internal article found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.internal_articles.retrieve_internal_article( + id=1, + ) + """ + _response = self._raw_client.retrieve_internal_article(id, request_options=request_options) + return _response.data + + def update_internal_article( + self, + id: int, + *, + title: typing.Optional[str] = OMIT, + body: typing.Optional[str] = OMIT, + author_id: typing.Optional[int] = OMIT, + owner_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> InternalArticle: + """ + You can update the details of a single internal article by making a PUT request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + id : int + The unique identifier for the internal article which is given by Intercom. + + title : typing.Optional[str] + The title of the article. + + body : typing.Optional[str] + The content of the article. + + author_id : typing.Optional[int] + The id of the author of the article. + + owner_id : typing.Optional[int] + The id of the author of the article. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + InternalArticle + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.internal_articles.update_internal_article( + id=1, + title="Christmas is here!", + body="

New gifts in store for the jolly season

", + ) + """ + _response = self._raw_client.update_internal_article( + id, title=title, body=body, author_id=author_id, owner_id=owner_id, request_options=request_options + ) + return _response.data + + def delete_internal_article( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeletedInternalArticleObject: + """ + You can delete a single internal article by making a DELETE request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + id : int + The unique identifier for the internal article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeletedInternalArticleObject + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.internal_articles.delete_internal_article( + id=1, + ) + """ + _response = self._raw_client.delete_internal_article(id, request_options=request_options) + return _response.data + + def search_internal_articles( + self, *, folder_id: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None + ) -> InternalArticleSearchResponse: + """ + You can search for internal articles by making a GET request to `https://api.intercom.io/internal_articles/search`. + + Parameters + ---------- + folder_id : typing.Optional[str] + The ID of the folder to search in. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + InternalArticleSearchResponse + Search successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.internal_articles.search_internal_articles( + folder_id="folder_id", + ) + """ + _response = self._raw_client.search_internal_articles(folder_id=folder_id, request_options=request_options) + return _response.data + + +class AsyncInternalArticlesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawInternalArticlesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawInternalArticlesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawInternalArticlesClient + """ + return self._raw_client + + async def list_internal_articles( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> InternalArticleList: + """ + You can fetch a list of all internal articles by making a GET request to `https://api.intercom.io/internal_articles`. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + InternalArticleList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.internal_articles.list_internal_articles() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_internal_articles(request_options=request_options) + return _response.data + + async def create_internal_article( + self, + *, + request: typing.Optional[CreateInternalArticleRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> InternalArticle: + """ + You can create a new internal article by making a POST request to `https://api.intercom.io/internal_articles`. + + Parameters + ---------- + request : typing.Optional[CreateInternalArticleRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + InternalArticle + internal article created + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.unstable import CreateInternalArticleRequest + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.internal_articles.create_internal_article( + request=CreateInternalArticleRequest( + title="Thanks for everything", + body="Body of the Article", + author_id=991266252, + owner_id=991266252, + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_internal_article(request=request, request_options=request_options) + return _response.data + + async def retrieve_internal_article( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> InternalArticle: + """ + You can fetch the details of a single internal article by making a GET request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + InternalArticle + Internal article found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.internal_articles.retrieve_internal_article( + id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.retrieve_internal_article(id, request_options=request_options) + return _response.data + + async def update_internal_article( + self, + id: int, + *, + title: typing.Optional[str] = OMIT, + body: typing.Optional[str] = OMIT, + author_id: typing.Optional[int] = OMIT, + owner_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> InternalArticle: + """ + You can update the details of a single internal article by making a PUT request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + id : int + The unique identifier for the internal article which is given by Intercom. + + title : typing.Optional[str] + The title of the article. + + body : typing.Optional[str] + The content of the article. + + author_id : typing.Optional[int] + The id of the author of the article. + + owner_id : typing.Optional[int] + The id of the author of the article. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + InternalArticle + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.internal_articles.update_internal_article( + id=1, + title="Christmas is here!", + body="

New gifts in store for the jolly season

", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update_internal_article( + id, title=title, body=body, author_id=author_id, owner_id=owner_id, request_options=request_options + ) + return _response.data + + async def delete_internal_article( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeletedInternalArticleObject: + """ + You can delete a single internal article by making a DELETE request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + id : int + The unique identifier for the internal article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeletedInternalArticleObject + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.internal_articles.delete_internal_article( + id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_internal_article(id, request_options=request_options) + return _response.data + + async def search_internal_articles( + self, *, folder_id: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None + ) -> InternalArticleSearchResponse: + """ + You can search for internal articles by making a GET request to `https://api.intercom.io/internal_articles/search`. + + Parameters + ---------- + folder_id : typing.Optional[str] + The ID of the folder to search in. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + InternalArticleSearchResponse + Search successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.internal_articles.search_internal_articles( + folder_id="folder_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.search_internal_articles( + folder_id=folder_id, request_options=request_options + ) + return _response.data diff --git a/src/intercom/unstable/internal_articles/raw_client.py b/src/intercom/unstable/internal_articles/raw_client.py new file mode 100644 index 00000000..15142492 --- /dev/null +++ b/src/intercom/unstable/internal_articles/raw_client.py @@ -0,0 +1,798 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.serialization import convert_and_respect_annotation_metadata +from ...core.unchecked_base_model import construct_type +from ..articles.types.internal_article import InternalArticle +from ..errors.bad_request_error import BadRequestError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.create_internal_article_request import CreateInternalArticleRequest +from ..types.deleted_internal_article_object import DeletedInternalArticleObject +from ..types.error import Error +from ..types.internal_article_list import InternalArticleList +from .types.internal_article_search_response import InternalArticleSearchResponse + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawInternalArticlesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_internal_articles( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[InternalArticleList]: + """ + You can fetch a list of all internal articles by making a GET request to `https://api.intercom.io/internal_articles`. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[InternalArticleList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "internal_articles", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + InternalArticleList, + construct_type( + type_=InternalArticleList, # 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 create_internal_article( + self, + *, + request: typing.Optional[CreateInternalArticleRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[InternalArticle]: + """ + You can create a new internal article by making a POST request to `https://api.intercom.io/internal_articles`. + + Parameters + ---------- + request : typing.Optional[CreateInternalArticleRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[InternalArticle] + internal article created + """ + _response = self._client_wrapper.httpx_client.request( + "internal_articles", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateInternalArticleRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + InternalArticle, + construct_type( + type_=InternalArticle, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 retrieve_internal_article( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[InternalArticle]: + """ + You can fetch the details of a single internal article by making a GET request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[InternalArticle] + Internal article found + """ + _response = self._client_wrapper.httpx_client.request( + f"internal_articles/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + InternalArticle, + construct_type( + type_=InternalArticle, # 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 update_internal_article( + self, + id: int, + *, + title: typing.Optional[str] = OMIT, + body: typing.Optional[str] = OMIT, + author_id: typing.Optional[int] = OMIT, + owner_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[InternalArticle]: + """ + You can update the details of a single internal article by making a PUT request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + id : int + The unique identifier for the internal article which is given by Intercom. + + title : typing.Optional[str] + The title of the article. + + body : typing.Optional[str] + The content of the article. + + author_id : typing.Optional[int] + The id of the author of the article. + + owner_id : typing.Optional[int] + The id of the author of the article. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[InternalArticle] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"internal_articles/{jsonable_encoder(id)}", + method="PUT", + json={ + "title": title, + "body": body, + "author_id": author_id, + "owner_id": owner_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + InternalArticle, + construct_type( + type_=InternalArticle, # 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 delete_internal_article( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DeletedInternalArticleObject]: + """ + You can delete a single internal article by making a DELETE request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + id : int + The unique identifier for the internal article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeletedInternalArticleObject] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"internal_articles/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeletedInternalArticleObject, + construct_type( + type_=DeletedInternalArticleObject, # 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 search_internal_articles( + self, *, folder_id: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[InternalArticleSearchResponse]: + """ + You can search for internal articles by making a GET request to `https://api.intercom.io/internal_articles/search`. + + Parameters + ---------- + folder_id : typing.Optional[str] + The ID of the folder to search in. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[InternalArticleSearchResponse] + Search successful + """ + _response = self._client_wrapper.httpx_client.request( + "internal_articles/search", + method="GET", + params={ + "folder_id": folder_id, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + InternalArticleSearchResponse, + construct_type( + type_=InternalArticleSearchResponse, # 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) + + +class AsyncRawInternalArticlesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_internal_articles( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[InternalArticleList]: + """ + You can fetch a list of all internal articles by making a GET request to `https://api.intercom.io/internal_articles`. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[InternalArticleList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "internal_articles", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + InternalArticleList, + construct_type( + type_=InternalArticleList, # 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 create_internal_article( + self, + *, + request: typing.Optional[CreateInternalArticleRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[InternalArticle]: + """ + You can create a new internal article by making a POST request to `https://api.intercom.io/internal_articles`. + + Parameters + ---------- + request : typing.Optional[CreateInternalArticleRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[InternalArticle] + internal article created + """ + _response = await self._client_wrapper.httpx_client.request( + "internal_articles", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateInternalArticleRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + InternalArticle, + construct_type( + type_=InternalArticle, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 retrieve_internal_article( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[InternalArticle]: + """ + You can fetch the details of a single internal article by making a GET request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + id : int + The unique identifier for the article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[InternalArticle] + Internal article found + """ + _response = await self._client_wrapper.httpx_client.request( + f"internal_articles/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + InternalArticle, + construct_type( + type_=InternalArticle, # 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 update_internal_article( + self, + id: int, + *, + title: typing.Optional[str] = OMIT, + body: typing.Optional[str] = OMIT, + author_id: typing.Optional[int] = OMIT, + owner_id: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[InternalArticle]: + """ + You can update the details of a single internal article by making a PUT request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + id : int + The unique identifier for the internal article which is given by Intercom. + + title : typing.Optional[str] + The title of the article. + + body : typing.Optional[str] + The content of the article. + + author_id : typing.Optional[int] + The id of the author of the article. + + owner_id : typing.Optional[int] + The id of the author of the article. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[InternalArticle] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"internal_articles/{jsonable_encoder(id)}", + method="PUT", + json={ + "title": title, + "body": body, + "author_id": author_id, + "owner_id": owner_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + InternalArticle, + construct_type( + type_=InternalArticle, # 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 delete_internal_article( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DeletedInternalArticleObject]: + """ + You can delete a single internal article by making a DELETE request to `https://api.intercom.io/internal_articles/`. + + Parameters + ---------- + id : int + The unique identifier for the internal article which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeletedInternalArticleObject] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"internal_articles/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeletedInternalArticleObject, + construct_type( + type_=DeletedInternalArticleObject, # 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 search_internal_articles( + self, *, folder_id: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[InternalArticleSearchResponse]: + """ + You can search for internal articles by making a GET request to `https://api.intercom.io/internal_articles/search`. + + Parameters + ---------- + folder_id : typing.Optional[str] + The ID of the folder to search in. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[InternalArticleSearchResponse] + Search successful + """ + _response = await self._client_wrapper.httpx_client.request( + "internal_articles/search", + method="GET", + params={ + "folder_id": folder_id, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + InternalArticleSearchResponse, + construct_type( + type_=InternalArticleSearchResponse, # 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) diff --git a/src/intercom/unstable/internal_articles/types/__init__.py b/src/intercom/unstable/internal_articles/types/__init__.py new file mode 100644 index 00000000..8f1745ce --- /dev/null +++ b/src/intercom/unstable/internal_articles/types/__init__.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .internal_article_list_item import InternalArticleListItem + from .internal_article_search_response import InternalArticleSearchResponse + from .internal_article_search_response_data import InternalArticleSearchResponseData +_dynamic_imports: typing.Dict[str, str] = { + "InternalArticleListItem": ".internal_article_list_item", + "InternalArticleSearchResponse": ".internal_article_search_response", + "InternalArticleSearchResponseData": ".internal_article_search_response_data", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["InternalArticleListItem", "InternalArticleSearchResponse", "InternalArticleSearchResponseData"] diff --git a/src/intercom/unstable/internal_articles/types/internal_article_list_item.py b/src/intercom/unstable/internal_articles/types/internal_article_list_item.py new file mode 100644 index 00000000..2c246076 --- /dev/null +++ b/src/intercom/unstable/internal_articles/types/internal_article_list_item.py @@ -0,0 +1,67 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class InternalArticleListItem(UncheckedBaseModel): + """ + The data returned about your internal articles when you list them. + """ + + type: typing.Optional[typing.Literal["internal_article"]] = pydantic.Field(default=None) + """ + The type of object - `internal_article`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the article which is given by Intercom. + """ + + title: typing.Optional[str] = pydantic.Field(default=None) + """ + The title of the article. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The body of the article in HTML. + """ + + owner_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of the owner of the article. + """ + + author_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of the author of the article. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the article was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the article was last updated. + """ + + locale: typing.Optional[str] = pydantic.Field(default=None) + """ + The default locale of the article. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/internal_articles/types/internal_article_search_response.py b/src/intercom/unstable/internal_articles/types/internal_article_search_response.py new file mode 100644 index 00000000..fc34dd70 --- /dev/null +++ b/src/intercom/unstable/internal_articles/types/internal_article_search_response.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...types.cursor_pages import CursorPages +from .internal_article_search_response_data import InternalArticleSearchResponseData + + +class InternalArticleSearchResponse(UncheckedBaseModel): + """ + The results of an Internal Article search + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object - `list`. + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + The total number of Internal Articles matching the search query + """ + + data: typing.Optional[InternalArticleSearchResponseData] = pydantic.Field(default=None) + """ + An object containing the results of the search. + """ + + pages: typing.Optional[CursorPages] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/internal_articles/types/internal_article_search_response_data.py b/src/intercom/unstable/internal_articles/types/internal_article_search_response_data.py new file mode 100644 index 00000000..ebeffb37 --- /dev/null +++ b/src/intercom/unstable/internal_articles/types/internal_article_search_response_data.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...articles.types.internal_article import InternalArticle + + +class InternalArticleSearchResponseData(UncheckedBaseModel): + """ + An object containing the results of the search. + """ + + internal_articles: typing.Optional[typing.List[InternalArticle]] = pydantic.Field(default=None) + """ + An array of Internal Article objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/jobs/__init__.py b/src/intercom/unstable/jobs/__init__.py new file mode 100644 index 00000000..386352d5 --- /dev/null +++ b/src/intercom/unstable/jobs/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import Jobs, JobsStatus +_dynamic_imports: typing.Dict[str, str] = {"Jobs": ".types", "JobsStatus": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Jobs", "JobsStatus"] diff --git a/src/intercom/unstable/jobs/client.py b/src/intercom/unstable/jobs/client.py new file mode 100644 index 00000000..c93c23a3 --- /dev/null +++ b/src/intercom/unstable/jobs/client.py @@ -0,0 +1,110 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from .raw_client import AsyncRawJobsClient, RawJobsClient +from .types.jobs import Jobs + + +class JobsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawJobsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawJobsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawJobsClient + """ + return self._raw_client + + def status(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Jobs: + """ + Retrieve the status of job execution. + + Parameters + ---------- + id : str + The unique identifier for the job which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Jobs + Job execution status + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.jobs.status( + id="id", + ) + """ + _response = self._raw_client.status(id, request_options=request_options) + return _response.data + + +class AsyncJobsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawJobsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawJobsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawJobsClient + """ + return self._raw_client + + async def status(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Jobs: + """ + Retrieve the status of job execution. + + Parameters + ---------- + id : str + The unique identifier for the job which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Jobs + Job execution status + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.jobs.status( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.status(id, request_options=request_options) + return _response.data diff --git a/src/intercom/unstable/jobs/raw_client.py b/src/intercom/unstable/jobs/raw_client.py new file mode 100644 index 00000000..a903eea2 --- /dev/null +++ b/src/intercom/unstable/jobs/raw_client.py @@ -0,0 +1,145 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from .types.jobs import Jobs + + +class RawJobsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def status(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Jobs]: + """ + Retrieve the status of job execution. + + Parameters + ---------- + id : str + The unique identifier for the job which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Jobs] + Job execution status + """ + _response = self._client_wrapper.httpx_client.request( + f"jobs/status/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Jobs, + construct_type( + type_=Jobs, # 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) + + +class AsyncRawJobsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def status( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Jobs]: + """ + Retrieve the status of job execution. + + Parameters + ---------- + id : str + The unique identifier for the job which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Jobs] + Job execution status + """ + _response = await self._client_wrapper.httpx_client.request( + f"jobs/status/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Jobs, + construct_type( + type_=Jobs, # 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) diff --git a/src/intercom/unstable/jobs/types/__init__.py b/src/intercom/unstable/jobs/types/__init__.py new file mode 100644 index 00000000..b125152d --- /dev/null +++ b/src/intercom/unstable/jobs/types/__init__.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .jobs import Jobs + from .jobs_status import JobsStatus +_dynamic_imports: typing.Dict[str, str] = {"Jobs": ".jobs", "JobsStatus": ".jobs_status"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Jobs", "JobsStatus"] diff --git a/src/intercom/unstable/jobs/types/jobs.py b/src/intercom/unstable/jobs/types/jobs.py new file mode 100644 index 00000000..70eb142c --- /dev/null +++ b/src/intercom/unstable/jobs/types/jobs.py @@ -0,0 +1,58 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .jobs_status import JobsStatus + + +class Jobs(UncheckedBaseModel): + """ + Jobs are tasks that are processed asynchronously by the Intercom system after being enqueued via the API. This allows for efficient handling of operations that may take time to complete, such as data imports or exports. You can check the status of your jobs to monitor their progress and ensure they are completed successfully. + """ + + type: typing.Optional[typing.Literal["job"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + id: str = pydantic.Field() + """ + The id of the job that's currently being processed or has completed. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + API endpoint URL to check the job status. + """ + + status: typing.Optional[JobsStatus] = pydantic.Field(default=None) + """ + The status of the job execution. + """ + + resource_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of resource created during job execution. + """ + + resource_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the resource created during job execution (e.g. ticket id) + """ + + resource_url: typing.Optional[str] = pydantic.Field(default=None) + """ + The url of the resource created during job exeuction. Use this url to fetch the resource. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/jobs/types/jobs_status.py b/src/intercom/unstable/jobs/types/jobs_status.py new file mode 100644 index 00000000..e2b915af --- /dev/null +++ b/src/intercom/unstable/jobs/types/jobs_status.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +JobsStatus = typing.Union[typing.Literal["pending", "success", "failed"], typing.Any] diff --git a/src/intercom/unstable/macros/__init__.py b/src/intercom/unstable/macros/__init__.py new file mode 100644 index 00000000..910e8d16 --- /dev/null +++ b/src/intercom/unstable/macros/__init__.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import Macro, MacroAvailableOnItem, MacroList, MacroListPages, MacroListPagesNext, MacroVisibleTo +_dynamic_imports: typing.Dict[str, str] = { + "Macro": ".types", + "MacroAvailableOnItem": ".types", + "MacroList": ".types", + "MacroListPages": ".types", + "MacroListPagesNext": ".types", + "MacroVisibleTo": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Macro", "MacroAvailableOnItem", "MacroList", "MacroListPages", "MacroListPagesNext", "MacroVisibleTo"] diff --git a/src/intercom/unstable/macros/client.py b/src/intercom/unstable/macros/client.py new file mode 100644 index 00000000..56c42321 --- /dev/null +++ b/src/intercom/unstable/macros/client.py @@ -0,0 +1,279 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from .raw_client import AsyncRawMacrosClient, RawMacrosClient +from .types.macro import Macro +from .types.macro_list import MacroList + + +class MacrosClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawMacrosClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawMacrosClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawMacrosClient + """ + return self._raw_client + + def list_macros( + self, + *, + per_page: typing.Optional[int] = None, + starting_after: typing.Optional[str] = None, + updated_since: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> MacroList: + """ + You can fetch a list of all macros (saved replies) in your workspace for use in automating responses. + + The macros are returned in descending order by updated_at. + + **Pagination** + + This endpoint uses cursor-based pagination via the `starting_after` parameter. The cursor is a Base64-encoded JSON array containing `[updated_at, id]` of the last item from the previous page. + + **Placeholder Transformation** + + The API transforms Intercom placeholders to a more standard XML-like format: + - From: `{{user.name | fallback: 'there'}}` + - To: `` + + Parameters + ---------- + per_page : typing.Optional[int] + The number of results per page + + starting_after : typing.Optional[str] + Base64-encoded cursor containing [updated_at, id] for pagination + + updated_since : typing.Optional[int] + Unix timestamp to filter macros updated after this time + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MacroList + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.macros.list_macros( + per_page=1, + starting_after="WzE3MTk0OTM3NTcuMCwgIjEyMyJd", + updated_since=1000000, + ) + """ + _response = self._raw_client.list_macros( + per_page=per_page, + starting_after=starting_after, + updated_since=updated_since, + request_options=request_options, + ) + return _response.data + + def get_macro(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> typing.Optional[Macro]: + """ + You can fetch a single macro (saved reply) by its ID. The macro will only be returned if it is visible to the authenticated user based on its visibility settings. + + **Visibility Rules** + + A macro is returned based on its `visible_to` setting: + - `everyone`: Always visible to all team members + - `specific_teams`: Only visible if the authenticated user belongs to one of the teams specified in `visible_to_team_ids` + + If a macro exists but is not visible to the authenticated user, a 404 error is returned. + + **Placeholder Transformation** + + The API transforms Intercom placeholders to a more standard XML-like format in the `body` field: + - From: `{{user.name | fallback: 'there'}}` + - To: `` + + Default values in placeholders are HTML-escaped for security. + + Parameters + ---------- + id : str + The unique identifier of the macro + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Macro] + Macro found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.macros.get_macro( + id="123", + ) + """ + _response = self._raw_client.get_macro(id, request_options=request_options) + return _response.data + + +class AsyncMacrosClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawMacrosClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawMacrosClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawMacrosClient + """ + return self._raw_client + + async def list_macros( + self, + *, + per_page: typing.Optional[int] = None, + starting_after: typing.Optional[str] = None, + updated_since: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> MacroList: + """ + You can fetch a list of all macros (saved replies) in your workspace for use in automating responses. + + The macros are returned in descending order by updated_at. + + **Pagination** + + This endpoint uses cursor-based pagination via the `starting_after` parameter. The cursor is a Base64-encoded JSON array containing `[updated_at, id]` of the last item from the previous page. + + **Placeholder Transformation** + + The API transforms Intercom placeholders to a more standard XML-like format: + - From: `{{user.name | fallback: 'there'}}` + - To: `` + + Parameters + ---------- + per_page : typing.Optional[int] + The number of results per page + + starting_after : typing.Optional[str] + Base64-encoded cursor containing [updated_at, id] for pagination + + updated_since : typing.Optional[int] + Unix timestamp to filter macros updated after this time + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MacroList + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.macros.list_macros( + per_page=1, + starting_after="WzE3MTk0OTM3NTcuMCwgIjEyMyJd", + updated_since=1000000, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_macros( + per_page=per_page, + starting_after=starting_after, + updated_since=updated_since, + request_options=request_options, + ) + return _response.data + + async def get_macro( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[Macro]: + """ + You can fetch a single macro (saved reply) by its ID. The macro will only be returned if it is visible to the authenticated user based on its visibility settings. + + **Visibility Rules** + + A macro is returned based on its `visible_to` setting: + - `everyone`: Always visible to all team members + - `specific_teams`: Only visible if the authenticated user belongs to one of the teams specified in `visible_to_team_ids` + + If a macro exists but is not visible to the authenticated user, a 404 error is returned. + + **Placeholder Transformation** + + The API transforms Intercom placeholders to a more standard XML-like format in the `body` field: + - From: `{{user.name | fallback: 'there'}}` + - To: `` + + Default values in placeholders are HTML-escaped for security. + + Parameters + ---------- + id : str + The unique identifier of the macro + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Macro] + Macro found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.macros.get_macro( + id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_macro(id, request_options=request_options) + return _response.data diff --git a/src/intercom/unstable/macros/raw_client.py b/src/intercom/unstable/macros/raw_client.py new file mode 100644 index 00000000..26b43a11 --- /dev/null +++ b/src/intercom/unstable/macros/raw_client.py @@ -0,0 +1,408 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.bad_request_error import BadRequestError +from ..errors.forbidden_error import ForbiddenError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from .types.macro import Macro +from .types.macro_list import MacroList + + +class RawMacrosClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_macros( + self, + *, + per_page: typing.Optional[int] = None, + starting_after: typing.Optional[str] = None, + updated_since: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[MacroList]: + """ + You can fetch a list of all macros (saved replies) in your workspace for use in automating responses. + + The macros are returned in descending order by updated_at. + + **Pagination** + + This endpoint uses cursor-based pagination via the `starting_after` parameter. The cursor is a Base64-encoded JSON array containing `[updated_at, id]` of the last item from the previous page. + + **Placeholder Transformation** + + The API transforms Intercom placeholders to a more standard XML-like format: + - From: `{{user.name | fallback: 'there'}}` + - To: `` + + Parameters + ---------- + per_page : typing.Optional[int] + The number of results per page + + starting_after : typing.Optional[str] + Base64-encoded cursor containing [updated_at, id] for pagination + + updated_since : typing.Optional[int] + Unix timestamp to filter macros updated after this time + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[MacroList] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "macros", + method="GET", + params={ + "per_page": per_page, + "starting_after": starting_after, + "updated_since": updated_since, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + MacroList, + construct_type( + type_=MacroList, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 == 403: + raise ForbiddenError( + 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 get_macro( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[typing.Optional[Macro]]: + """ + You can fetch a single macro (saved reply) by its ID. The macro will only be returned if it is visible to the authenticated user based on its visibility settings. + + **Visibility Rules** + + A macro is returned based on its `visible_to` setting: + - `everyone`: Always visible to all team members + - `specific_teams`: Only visible if the authenticated user belongs to one of the teams specified in `visible_to_team_ids` + + If a macro exists but is not visible to the authenticated user, a 404 error is returned. + + **Placeholder Transformation** + + The API transforms Intercom placeholders to a more standard XML-like format in the `body` field: + - From: `{{user.name | fallback: 'there'}}` + - To: `` + + Default values in placeholders are HTML-escaped for security. + + Parameters + ---------- + id : str + The unique identifier of the macro + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[Macro]] + Macro found + """ + _response = self._client_wrapper.httpx_client.request( + f"macros/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Macro], + construct_type( + type_=typing.Optional[Macro], # 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 == 403: + raise ForbiddenError( + 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) + + +class AsyncRawMacrosClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_macros( + self, + *, + per_page: typing.Optional[int] = None, + starting_after: typing.Optional[str] = None, + updated_since: typing.Optional[int] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[MacroList]: + """ + You can fetch a list of all macros (saved replies) in your workspace for use in automating responses. + + The macros are returned in descending order by updated_at. + + **Pagination** + + This endpoint uses cursor-based pagination via the `starting_after` parameter. The cursor is a Base64-encoded JSON array containing `[updated_at, id]` of the last item from the previous page. + + **Placeholder Transformation** + + The API transforms Intercom placeholders to a more standard XML-like format: + - From: `{{user.name | fallback: 'there'}}` + - To: `` + + Parameters + ---------- + per_page : typing.Optional[int] + The number of results per page + + starting_after : typing.Optional[str] + Base64-encoded cursor containing [updated_at, id] for pagination + + updated_since : typing.Optional[int] + Unix timestamp to filter macros updated after this time + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[MacroList] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "macros", + method="GET", + params={ + "per_page": per_page, + "starting_after": starting_after, + "updated_since": updated_since, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + MacroList, + construct_type( + type_=MacroList, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 == 403: + raise ForbiddenError( + 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 get_macro( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[typing.Optional[Macro]]: + """ + You can fetch a single macro (saved reply) by its ID. The macro will only be returned if it is visible to the authenticated user based on its visibility settings. + + **Visibility Rules** + + A macro is returned based on its `visible_to` setting: + - `everyone`: Always visible to all team members + - `specific_teams`: Only visible if the authenticated user belongs to one of the teams specified in `visible_to_team_ids` + + If a macro exists but is not visible to the authenticated user, a 404 error is returned. + + **Placeholder Transformation** + + The API transforms Intercom placeholders to a more standard XML-like format in the `body` field: + - From: `{{user.name | fallback: 'there'}}` + - To: `` + + Default values in placeholders are HTML-escaped for security. + + Parameters + ---------- + id : str + The unique identifier of the macro + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[Macro]] + Macro found + """ + _response = await self._client_wrapper.httpx_client.request( + f"macros/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Macro], + construct_type( + type_=typing.Optional[Macro], # 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 == 403: + raise ForbiddenError( + 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) diff --git a/src/intercom/unstable/macros/types/__init__.py b/src/intercom/unstable/macros/types/__init__.py new file mode 100644 index 00000000..240b7d1d --- /dev/null +++ b/src/intercom/unstable/macros/types/__init__.py @@ -0,0 +1,46 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .macro import Macro + from .macro_available_on_item import MacroAvailableOnItem + from .macro_list import MacroList + from .macro_list_pages import MacroListPages + from .macro_list_pages_next import MacroListPagesNext + from .macro_visible_to import MacroVisibleTo +_dynamic_imports: typing.Dict[str, str] = { + "Macro": ".macro", + "MacroAvailableOnItem": ".macro_available_on_item", + "MacroList": ".macro_list", + "MacroListPages": ".macro_list_pages", + "MacroListPagesNext": ".macro_list_pages_next", + "MacroVisibleTo": ".macro_visible_to", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Macro", "MacroAvailableOnItem", "MacroList", "MacroListPages", "MacroListPagesNext", "MacroVisibleTo"] diff --git a/src/intercom/unstable/macros/types/macro.py b/src/intercom/unstable/macros/types/macro.py new file mode 100644 index 00000000..a36f80e8 --- /dev/null +++ b/src/intercom/unstable/macros/types/macro.py @@ -0,0 +1,75 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .macro_available_on_item import MacroAvailableOnItem +from .macro_visible_to import MacroVisibleTo + + +class Macro(UncheckedBaseModel): + """ + A macro is a pre-defined response template (saved reply) that can be used to quickly reply to conversations. + """ + + type: typing.Optional[typing.Literal["macro"]] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `macro`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the macro. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the macro. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The body of the macro in HTML format with placeholders transformed to XML-like format. + """ + + body_text: typing.Optional[str] = pydantic.Field(default=None) + """ + The plain text version of the macro body with original Intercom placeholder format. + """ + + created_at: typing.Optional[dt.datetime] = pydantic.Field(default=None) + """ + The time the macro was created in ISO 8601 format. + """ + + updated_at: typing.Optional[dt.datetime] = pydantic.Field(default=None) + """ + The time the macro was last updated in ISO 8601 format. + """ + + visible_to: typing.Optional[MacroVisibleTo] = pydantic.Field(default=None) + """ + Who can view this macro. + """ + + visible_to_team_ids: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + The team IDs that can view this macro when visible_to is set to specific_teams. + """ + + available_on: typing.Optional[typing.List[MacroAvailableOnItem]] = pydantic.Field(default=None) + """ + Where the macro is available for use. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/macros/types/macro_available_on_item.py b/src/intercom/unstable/macros/types/macro_available_on_item.py new file mode 100644 index 00000000..245098a4 --- /dev/null +++ b/src/intercom/unstable/macros/types/macro_available_on_item.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +MacroAvailableOnItem = typing.Union[typing.Literal["inbox", "messenger"], typing.Any] diff --git a/src/intercom/unstable/macros/types/macro_list.py b/src/intercom/unstable/macros/types/macro_list.py new file mode 100644 index 00000000..b00cf245 --- /dev/null +++ b/src/intercom/unstable/macros/types/macro_list.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .macro import Macro +from .macro_list_pages import MacroListPages + + +class MacroList(UncheckedBaseModel): + """ + A paginated list of macros (saved replies) in the workspace. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + Always list + """ + + data: typing.Optional[typing.List[typing.Optional[Macro]]] = pydantic.Field(default=None) + """ + The list of macro objects + """ + + pages: typing.Optional[MacroListPages] = pydantic.Field(default=None) + """ + Pagination information + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/macros/types/macro_list_pages.py b/src/intercom/unstable/macros/types/macro_list_pages.py new file mode 100644 index 00000000..cf734b37 --- /dev/null +++ b/src/intercom/unstable/macros/types/macro_list_pages.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .macro_list_pages_next import MacroListPagesNext + + +class MacroListPages(UncheckedBaseModel): + """ + Pagination information + """ + + type: typing.Optional[typing.Literal["pages"]] = pydantic.Field(default=None) + """ + The type of pagination + """ + + per_page: typing.Optional[int] = pydantic.Field(default=None) + """ + Number of results per page + """ + + next: typing.Optional[MacroListPagesNext] = pydantic.Field(default=None) + """ + Cursor for the next page + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/macros/types/macro_list_pages_next.py b/src/intercom/unstable/macros/types/macro_list_pages_next.py new file mode 100644 index 00000000..4ce6398d --- /dev/null +++ b/src/intercom/unstable/macros/types/macro_list_pages_next.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class MacroListPagesNext(UncheckedBaseModel): + """ + Cursor for the next page + """ + + starting_after: typing.Optional[str] = pydantic.Field(default=None) + """ + Base64-encoded cursor containing [updated_at, id] for pagination + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/macros/types/macro_visible_to.py b/src/intercom/unstable/macros/types/macro_visible_to.py new file mode 100644 index 00000000..fd8477c1 --- /dev/null +++ b/src/intercom/unstable/macros/types/macro_visible_to.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +MacroVisibleTo = typing.Union[typing.Literal["everyone", "specific_teams"], typing.Any] diff --git a/src/intercom/unstable/messages/__init__.py b/src/intercom/unstable/messages/__init__.py new file mode 100644 index 00000000..ad071d44 --- /dev/null +++ b/src/intercom/unstable/messages/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import Message, MessageMessageType +_dynamic_imports: typing.Dict[str, str] = {"Message": ".types", "MessageMessageType": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Message", "MessageMessageType"] diff --git a/src/intercom/unstable/messages/client.py b/src/intercom/unstable/messages/client.py new file mode 100644 index 00000000..e698f04a --- /dev/null +++ b/src/intercom/unstable/messages/client.py @@ -0,0 +1,263 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..types.create_message_request import CreateMessageRequest +from ..types.whatsapp_message_status_list import WhatsappMessageStatusList +from .raw_client import AsyncRawMessagesClient, RawMessagesClient +from .types.message import Message + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class MessagesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawMessagesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawMessagesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawMessagesClient + """ + return self._raw_client + + def create_message( + self, + *, + request: typing.Optional[CreateMessageRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Message: + """ + You can create a message that has been initiated by an admin. The conversation can be either an in-app message, an email, sms or whatsapp. + + > 🚧 Sending for visitors + > + > There can be a short delay between when a contact is created and when a contact becomes available to be messaged through the API. A 404 Not Found error will be returned in this case. + + This will return the Message model that has been created. + + > 🚧 Retrieving Associated Conversations + > + > As this is a message, there will be no conversation present until the contact responds. Once they do, you will have to search for a contact's conversations with the id of the message. + + Parameters + ---------- + request : typing.Optional[CreateMessageRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Message + admin message created + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.messages.create_message( + request={ + "from": {"type": "user", "id": "6762f2341bb69f9f2193bc17"}, + "body": "heyy", + "referer": "https://twitter.com/bob", + }, + ) + """ + _response = self._raw_client.create_message(request=request, request_options=request_options) + return _response.data + + def get_whats_app_message_status( + self, + *, + ruleset_id: str, + per_page: typing.Optional[int] = None, + starting_after: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> WhatsappMessageStatusList: + """ + Retrieves statuses of messages sent from the Outbound module. Currently, this API only supports WhatsApp messages. + + + This endpoint returns paginated status events for WhatsApp messages sent via the Outbound module, providing + information about delivery state and related message details. + + Parameters + ---------- + ruleset_id : str + The unique identifier for the set of messages to check status for + + per_page : typing.Optional[int] + Number of results per page (default 50, max 100) + + starting_after : typing.Optional[str] + Cursor for pagination, used to fetch the next page of results + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + WhatsappMessageStatusList + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.messages.get_whats_app_message_status( + ruleset_id="ruleset_id", + per_page=1, + starting_after="starting_after", + ) + """ + _response = self._raw_client.get_whats_app_message_status( + ruleset_id=ruleset_id, per_page=per_page, starting_after=starting_after, request_options=request_options + ) + return _response.data + + +class AsyncMessagesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawMessagesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawMessagesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawMessagesClient + """ + return self._raw_client + + async def create_message( + self, + *, + request: typing.Optional[CreateMessageRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> Message: + """ + You can create a message that has been initiated by an admin. The conversation can be either an in-app message, an email, sms or whatsapp. + + > 🚧 Sending for visitors + > + > There can be a short delay between when a contact is created and when a contact becomes available to be messaged through the API. A 404 Not Found error will be returned in this case. + + This will return the Message model that has been created. + + > 🚧 Retrieving Associated Conversations + > + > As this is a message, there will be no conversation present until the contact responds. Once they do, you will have to search for a contact's conversations with the id of the message. + + Parameters + ---------- + request : typing.Optional[CreateMessageRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Message + admin message created + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.messages.create_message( + request={ + "from": {"type": "user", "id": "6762f2341bb69f9f2193bc17"}, + "body": "heyy", + "referer": "https://twitter.com/bob", + }, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_message(request=request, request_options=request_options) + return _response.data + + async def get_whats_app_message_status( + self, + *, + ruleset_id: str, + per_page: typing.Optional[int] = None, + starting_after: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> WhatsappMessageStatusList: + """ + Retrieves statuses of messages sent from the Outbound module. Currently, this API only supports WhatsApp messages. + + + This endpoint returns paginated status events for WhatsApp messages sent via the Outbound module, providing + information about delivery state and related message details. + + Parameters + ---------- + ruleset_id : str + The unique identifier for the set of messages to check status for + + per_page : typing.Optional[int] + Number of results per page (default 50, max 100) + + starting_after : typing.Optional[str] + Cursor for pagination, used to fetch the next page of results + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + WhatsappMessageStatusList + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.messages.get_whats_app_message_status( + ruleset_id="ruleset_id", + per_page=1, + starting_after="starting_after", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_whats_app_message_status( + ruleset_id=ruleset_id, per_page=per_page, starting_after=starting_after, request_options=request_options + ) + return _response.data diff --git a/src/intercom/unstable/messages/raw_client.py b/src/intercom/unstable/messages/raw_client.py new file mode 100644 index 00000000..86091c29 --- /dev/null +++ b/src/intercom/unstable/messages/raw_client.py @@ -0,0 +1,438 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.http_response import AsyncHttpResponse, HttpResponse +from ...core.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.bad_request_error import BadRequestError +from ..errors.forbidden_error import ForbiddenError +from ..errors.internal_server_error import InternalServerError +from ..errors.unauthorized_error import UnauthorizedError +from ..errors.unprocessable_entity_error import UnprocessableEntityError +from ..types.create_message_request import CreateMessageRequest +from ..types.error import Error +from ..types.whatsapp_message_status_list import WhatsappMessageStatusList +from .types.message import Message + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawMessagesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def create_message( + self, + *, + request: typing.Optional[CreateMessageRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Message]: + """ + You can create a message that has been initiated by an admin. The conversation can be either an in-app message, an email, sms or whatsapp. + + > 🚧 Sending for visitors + > + > There can be a short delay between when a contact is created and when a contact becomes available to be messaged through the API. A 404 Not Found error will be returned in this case. + + This will return the Message model that has been created. + + > 🚧 Retrieving Associated Conversations + > + > As this is a message, there will be no conversation present until the contact responds. Once they do, you will have to search for a contact's conversations with the id of the message. + + Parameters + ---------- + request : typing.Optional[CreateMessageRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Message] + admin message created + """ + _response = self._client_wrapper.httpx_client.request( + "messages", + method="POST", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Message, + construct_type( + type_=Message, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + Error, + construct_type( + type_=Error, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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 get_whats_app_message_status( + self, + *, + ruleset_id: str, + per_page: typing.Optional[int] = None, + starting_after: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[WhatsappMessageStatusList]: + """ + Retrieves statuses of messages sent from the Outbound module. Currently, this API only supports WhatsApp messages. + + + This endpoint returns paginated status events for WhatsApp messages sent via the Outbound module, providing + information about delivery state and related message details. + + Parameters + ---------- + ruleset_id : str + The unique identifier for the set of messages to check status for + + per_page : typing.Optional[int] + Number of results per page (default 50, max 100) + + starting_after : typing.Optional[str] + Cursor for pagination, used to fetch the next page of results + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[WhatsappMessageStatusList] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "messages/status", + method="GET", + params={ + "ruleset_id": ruleset_id, + "per_page": per_page, + "starting_after": starting_after, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + WhatsappMessageStatusList, + construct_type( + type_=WhatsappMessageStatusList, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + Error, + construct_type( + type_=Error, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + 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) + + +class AsyncRawMessagesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def create_message( + self, + *, + request: typing.Optional[CreateMessageRequest] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Message]: + """ + You can create a message that has been initiated by an admin. The conversation can be either an in-app message, an email, sms or whatsapp. + + > 🚧 Sending for visitors + > + > There can be a short delay between when a contact is created and when a contact becomes available to be messaged through the API. A 404 Not Found error will be returned in this case. + + This will return the Message model that has been created. + + > 🚧 Retrieving Associated Conversations + > + > As this is a message, there will be no conversation present until the contact responds. Once they do, you will have to search for a contact's conversations with the id of the message. + + Parameters + ---------- + request : typing.Optional[CreateMessageRequest] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Message] + admin message created + """ + _response = await self._client_wrapper.httpx_client.request( + "messages", + method="POST", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Message, + construct_type( + type_=Message, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + Error, + construct_type( + type_=Error, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 422: + raise UnprocessableEntityError( + 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 get_whats_app_message_status( + self, + *, + ruleset_id: str, + per_page: typing.Optional[int] = None, + starting_after: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[WhatsappMessageStatusList]: + """ + Retrieves statuses of messages sent from the Outbound module. Currently, this API only supports WhatsApp messages. + + + This endpoint returns paginated status events for WhatsApp messages sent via the Outbound module, providing + information about delivery state and related message details. + + Parameters + ---------- + ruleset_id : str + The unique identifier for the set of messages to check status for + + per_page : typing.Optional[int] + Number of results per page (default 50, max 100) + + starting_after : typing.Optional[str] + Cursor for pagination, used to fetch the next page of results + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[WhatsappMessageStatusList] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "messages/status", + method="GET", + params={ + "ruleset_id": ruleset_id, + "per_page": per_page, + "starting_after": starting_after, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + WhatsappMessageStatusList, + construct_type( + type_=WhatsappMessageStatusList, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + Error, + construct_type( + type_=Error, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 500: + raise InternalServerError( + 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) diff --git a/src/intercom/unstable/messages/types/__init__.py b/src/intercom/unstable/messages/types/__init__.py new file mode 100644 index 00000000..cfd041bb --- /dev/null +++ b/src/intercom/unstable/messages/types/__init__.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .message import Message + from .message_message_type import MessageMessageType +_dynamic_imports: typing.Dict[str, str] = {"Message": ".message", "MessageMessageType": ".message_message_type"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Message", "MessageMessageType"] diff --git a/src/intercom/unstable/messages/types/message.py b/src/intercom/unstable/messages/types/message.py new file mode 100644 index 00000000..bbf7dce9 --- /dev/null +++ b/src/intercom/unstable/messages/types/message.py @@ -0,0 +1,58 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .message_message_type import MessageMessageType + + +class Message(UncheckedBaseModel): + """ + Message are how you reach out to contacts in Intercom. They are created when an admin sends an outbound message to a contact. + """ + + type: str = pydantic.Field() + """ + The type of the message + """ + + id: str = pydantic.Field() + """ + The id representing the message. + """ + + created_at: int = pydantic.Field() + """ + The time the conversation was created. + """ + + subject: typing.Optional[str] = pydantic.Field(default=None) + """ + The subject of the message. Only present if message_type: email. + """ + + body: str = pydantic.Field() + """ + The message body, which may contain HTML. + """ + + message_type: MessageMessageType = pydantic.Field() + """ + The type of message that was sent. Can be email, inapp, facebook, twitter, sms or whatsapp. + """ + + conversation_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The associated conversation_id + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/messages/types/message_message_type.py b/src/intercom/unstable/messages/types/message_message_type.py new file mode 100644 index 00000000..c23f863c --- /dev/null +++ b/src/intercom/unstable/messages/types/message_message_type.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +MessageMessageType = typing.Union[ + typing.Literal["email", "inapp", "facebook", "twitter", "sms", "whatsapp"], typing.Any +] diff --git a/src/intercom/unstable/news/__init__.py b/src/intercom/unstable/news/__init__.py new file mode 100644 index 00000000..2b5fec14 --- /dev/null +++ b/src/intercom/unstable/news/__init__.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import NewsItem, NewsItemState, Newsfeed, NewsfeedAssignment +_dynamic_imports: typing.Dict[str, str] = { + "NewsItem": ".types", + "NewsItemState": ".types", + "Newsfeed": ".types", + "NewsfeedAssignment": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["NewsItem", "NewsItemState", "Newsfeed", "NewsfeedAssignment"] diff --git a/src/intercom/unstable/news/client.py b/src/intercom/unstable/news/client.py new file mode 100644 index 00000000..9fa6db62 --- /dev/null +++ b/src/intercom/unstable/news/client.py @@ -0,0 +1,806 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..types.deleted_object import DeletedObject +from ..types.news_item_request_state import NewsItemRequestState +from ..types.paginated_response import PaginatedResponse +from .raw_client import AsyncRawNewsClient, RawNewsClient +from .types.news_item import NewsItem +from .types.newsfeed import Newsfeed +from .types.newsfeed_assignment import NewsfeedAssignment + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class NewsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawNewsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawNewsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawNewsClient + """ + return self._raw_client + + def list_news_items(self, *, request_options: typing.Optional[RequestOptions] = None) -> PaginatedResponse: + """ + You can fetch a list of all news items + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.news.list_news_items() + """ + _response = self._raw_client.list_news_items(request_options=request_options) + return _response.data + + def create_news_item( + self, + *, + title: str, + sender_id: int, + body: typing.Optional[str] = OMIT, + state: typing.Optional[NewsItemRequestState] = OMIT, + deliver_silently: typing.Optional[bool] = OMIT, + labels: typing.Optional[typing.Sequence[str]] = OMIT, + reactions: typing.Optional[typing.Sequence[typing.Optional[str]]] = OMIT, + newsfeed_assignments: typing.Optional[typing.Sequence[NewsfeedAssignment]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> NewsItem: + """ + You can create a news item + + Parameters + ---------- + title : str + The title of the news item. + + sender_id : int + The id of the sender of the news item. Must be a teammate on the workspace. + + body : typing.Optional[str] + The news item body, which may contain HTML. + + state : typing.Optional[NewsItemRequestState] + News items will not be visible to your users in the assigned newsfeeds until they are set live. + + deliver_silently : typing.Optional[bool] + When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + + labels : typing.Optional[typing.Sequence[str]] + Label names displayed to users to categorize the news item. + + reactions : typing.Optional[typing.Sequence[typing.Optional[str]]] + Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + + newsfeed_assignments : typing.Optional[typing.Sequence[NewsfeedAssignment]] + A list of newsfeed_assignments to assign to the specified newsfeed. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + NewsItem + successful + + Examples + -------- + from intercom import Intercom + from intercom.unstable.news import NewsfeedAssignment + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.news.create_news_item( + title="Halloween is here!", + body="

New costumes in store for this spooky season

", + sender_id=991267834, + state="live", + deliver_silently=True, + labels=["Product", "Update", "New"], + reactions=["😆", "😅"], + newsfeed_assignments=[ + NewsfeedAssignment( + newsfeed_id=53, + published_at=1664638214, + ) + ], + ) + """ + _response = self._raw_client.create_news_item( + title=title, + sender_id=sender_id, + body=body, + state=state, + deliver_silently=deliver_silently, + labels=labels, + reactions=reactions, + newsfeed_assignments=newsfeed_assignments, + request_options=request_options, + ) + return _response.data + + def retrieve_news_item(self, id: int, *, request_options: typing.Optional[RequestOptions] = None) -> NewsItem: + """ + You can fetch the details of a single news item. + + Parameters + ---------- + id : int + The unique identifier for the news item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + NewsItem + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.news.retrieve_news_item( + id=1, + ) + """ + _response = self._raw_client.retrieve_news_item(id, request_options=request_options) + return _response.data + + def update_news_item( + self, + id: int, + *, + title: str, + sender_id: int, + body: typing.Optional[str] = OMIT, + state: typing.Optional[NewsItemRequestState] = OMIT, + deliver_silently: typing.Optional[bool] = OMIT, + labels: typing.Optional[typing.Sequence[str]] = OMIT, + reactions: typing.Optional[typing.Sequence[typing.Optional[str]]] = OMIT, + newsfeed_assignments: typing.Optional[typing.Sequence[NewsfeedAssignment]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> NewsItem: + """ + Parameters + ---------- + id : int + The unique identifier for the news item which is given by Intercom. + + title : str + The title of the news item. + + sender_id : int + The id of the sender of the news item. Must be a teammate on the workspace. + + body : typing.Optional[str] + The news item body, which may contain HTML. + + state : typing.Optional[NewsItemRequestState] + News items will not be visible to your users in the assigned newsfeeds until they are set live. + + deliver_silently : typing.Optional[bool] + When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + + labels : typing.Optional[typing.Sequence[str]] + Label names displayed to users to categorize the news item. + + reactions : typing.Optional[typing.Sequence[typing.Optional[str]]] + Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + + newsfeed_assignments : typing.Optional[typing.Sequence[NewsfeedAssignment]] + A list of newsfeed_assignments to assign to the specified newsfeed. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + NewsItem + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.news.update_news_item( + id=1, + title="Christmas is here!", + body="

New gifts in store for the jolly season

", + sender_id=991267845, + reactions=["😝", "😂"], + ) + """ + _response = self._raw_client.update_news_item( + id, + title=title, + sender_id=sender_id, + body=body, + state=state, + deliver_silently=deliver_silently, + labels=labels, + reactions=reactions, + newsfeed_assignments=newsfeed_assignments, + request_options=request_options, + ) + return _response.data + + def delete_news_item(self, id: int, *, request_options: typing.Optional[RequestOptions] = None) -> DeletedObject: + """ + You can delete a single news item. + + Parameters + ---------- + id : int + The unique identifier for the news item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeletedObject + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.news.delete_news_item( + id=1, + ) + """ + _response = self._raw_client.delete_news_item(id, request_options=request_options) + return _response.data + + def list_live_newsfeed_items( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> PaginatedResponse: + """ + You can fetch a list of all news items that are live on a given newsfeed + + Parameters + ---------- + id : str + The unique identifier for the news feed item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.news.list_live_newsfeed_items( + id="123", + ) + """ + _response = self._raw_client.list_live_newsfeed_items(id, request_options=request_options) + return _response.data + + def list_newsfeeds(self, *, request_options: typing.Optional[RequestOptions] = None) -> PaginatedResponse: + """ + You can fetch a list of all newsfeeds + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.news.list_newsfeeds() + """ + _response = self._raw_client.list_newsfeeds(request_options=request_options) + return _response.data + + def retrieve_newsfeed(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Newsfeed: + """ + You can fetch the details of a single newsfeed + + Parameters + ---------- + id : str + The unique identifier for the news feed item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Newsfeed + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.news.retrieve_newsfeed( + id="123", + ) + """ + _response = self._raw_client.retrieve_newsfeed(id, request_options=request_options) + return _response.data + + +class AsyncNewsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawNewsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawNewsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawNewsClient + """ + return self._raw_client + + async def list_news_items(self, *, request_options: typing.Optional[RequestOptions] = None) -> PaginatedResponse: + """ + You can fetch a list of all news items + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.news.list_news_items() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_news_items(request_options=request_options) + return _response.data + + async def create_news_item( + self, + *, + title: str, + sender_id: int, + body: typing.Optional[str] = OMIT, + state: typing.Optional[NewsItemRequestState] = OMIT, + deliver_silently: typing.Optional[bool] = OMIT, + labels: typing.Optional[typing.Sequence[str]] = OMIT, + reactions: typing.Optional[typing.Sequence[typing.Optional[str]]] = OMIT, + newsfeed_assignments: typing.Optional[typing.Sequence[NewsfeedAssignment]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> NewsItem: + """ + You can create a news item + + Parameters + ---------- + title : str + The title of the news item. + + sender_id : int + The id of the sender of the news item. Must be a teammate on the workspace. + + body : typing.Optional[str] + The news item body, which may contain HTML. + + state : typing.Optional[NewsItemRequestState] + News items will not be visible to your users in the assigned newsfeeds until they are set live. + + deliver_silently : typing.Optional[bool] + When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + + labels : typing.Optional[typing.Sequence[str]] + Label names displayed to users to categorize the news item. + + reactions : typing.Optional[typing.Sequence[typing.Optional[str]]] + Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + + newsfeed_assignments : typing.Optional[typing.Sequence[NewsfeedAssignment]] + A list of newsfeed_assignments to assign to the specified newsfeed. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + NewsItem + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.unstable.news import NewsfeedAssignment + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.news.create_news_item( + title="Halloween is here!", + body="

New costumes in store for this spooky season

", + sender_id=991267834, + state="live", + deliver_silently=True, + labels=["Product", "Update", "New"], + reactions=["😆", "😅"], + newsfeed_assignments=[ + NewsfeedAssignment( + newsfeed_id=53, + published_at=1664638214, + ) + ], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_news_item( + title=title, + sender_id=sender_id, + body=body, + state=state, + deliver_silently=deliver_silently, + labels=labels, + reactions=reactions, + newsfeed_assignments=newsfeed_assignments, + request_options=request_options, + ) + return _response.data + + async def retrieve_news_item(self, id: int, *, request_options: typing.Optional[RequestOptions] = None) -> NewsItem: + """ + You can fetch the details of a single news item. + + Parameters + ---------- + id : int + The unique identifier for the news item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + NewsItem + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.news.retrieve_news_item( + id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.retrieve_news_item(id, request_options=request_options) + return _response.data + + async def update_news_item( + self, + id: int, + *, + title: str, + sender_id: int, + body: typing.Optional[str] = OMIT, + state: typing.Optional[NewsItemRequestState] = OMIT, + deliver_silently: typing.Optional[bool] = OMIT, + labels: typing.Optional[typing.Sequence[str]] = OMIT, + reactions: typing.Optional[typing.Sequence[typing.Optional[str]]] = OMIT, + newsfeed_assignments: typing.Optional[typing.Sequence[NewsfeedAssignment]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> NewsItem: + """ + Parameters + ---------- + id : int + The unique identifier for the news item which is given by Intercom. + + title : str + The title of the news item. + + sender_id : int + The id of the sender of the news item. Must be a teammate on the workspace. + + body : typing.Optional[str] + The news item body, which may contain HTML. + + state : typing.Optional[NewsItemRequestState] + News items will not be visible to your users in the assigned newsfeeds until they are set live. + + deliver_silently : typing.Optional[bool] + When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + + labels : typing.Optional[typing.Sequence[str]] + Label names displayed to users to categorize the news item. + + reactions : typing.Optional[typing.Sequence[typing.Optional[str]]] + Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + + newsfeed_assignments : typing.Optional[typing.Sequence[NewsfeedAssignment]] + A list of newsfeed_assignments to assign to the specified newsfeed. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + NewsItem + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.news.update_news_item( + id=1, + title="Christmas is here!", + body="

New gifts in store for the jolly season

", + sender_id=991267845, + reactions=["😝", "😂"], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update_news_item( + id, + title=title, + sender_id=sender_id, + body=body, + state=state, + deliver_silently=deliver_silently, + labels=labels, + reactions=reactions, + newsfeed_assignments=newsfeed_assignments, + request_options=request_options, + ) + return _response.data + + async def delete_news_item( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeletedObject: + """ + You can delete a single news item. + + Parameters + ---------- + id : int + The unique identifier for the news item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeletedObject + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.news.delete_news_item( + id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_news_item(id, request_options=request_options) + return _response.data + + async def list_live_newsfeed_items( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> PaginatedResponse: + """ + You can fetch a list of all news items that are live on a given newsfeed + + Parameters + ---------- + id : str + The unique identifier for the news feed item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.news.list_live_newsfeed_items( + id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_live_newsfeed_items(id, request_options=request_options) + return _response.data + + async def list_newsfeeds(self, *, request_options: typing.Optional[RequestOptions] = None) -> PaginatedResponse: + """ + You can fetch a list of all newsfeeds + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + PaginatedResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.news.list_newsfeeds() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_newsfeeds(request_options=request_options) + return _response.data + + async def retrieve_newsfeed(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Newsfeed: + """ + You can fetch the details of a single newsfeed + + Parameters + ---------- + id : str + The unique identifier for the news feed item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Newsfeed + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.news.retrieve_newsfeed( + id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.retrieve_newsfeed(id, request_options=request_options) + return _response.data diff --git a/src/intercom/unstable/news/raw_client.py b/src/intercom/unstable/news/raw_client.py new file mode 100644 index 00000000..7ff1e010 --- /dev/null +++ b/src/intercom/unstable/news/raw_client.py @@ -0,0 +1,1080 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.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 ..types.deleted_object import DeletedObject +from ..types.error import Error +from ..types.news_item_request_state import NewsItemRequestState +from ..types.paginated_response import PaginatedResponse +from .types.news_item import NewsItem +from .types.newsfeed import Newsfeed +from .types.newsfeed_assignment import NewsfeedAssignment + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawNewsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_news_items( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[PaginatedResponse]: + """ + You can fetch a list of all news items + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[PaginatedResponse] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "news/news_items", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedResponse, + construct_type( + type_=PaginatedResponse, # 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 create_news_item( + self, + *, + title: str, + sender_id: int, + body: typing.Optional[str] = OMIT, + state: typing.Optional[NewsItemRequestState] = OMIT, + deliver_silently: typing.Optional[bool] = OMIT, + labels: typing.Optional[typing.Sequence[str]] = OMIT, + reactions: typing.Optional[typing.Sequence[typing.Optional[str]]] = OMIT, + newsfeed_assignments: typing.Optional[typing.Sequence[NewsfeedAssignment]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[NewsItem]: + """ + You can create a news item + + Parameters + ---------- + title : str + The title of the news item. + + sender_id : int + The id of the sender of the news item. Must be a teammate on the workspace. + + body : typing.Optional[str] + The news item body, which may contain HTML. + + state : typing.Optional[NewsItemRequestState] + News items will not be visible to your users in the assigned newsfeeds until they are set live. + + deliver_silently : typing.Optional[bool] + When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + + labels : typing.Optional[typing.Sequence[str]] + Label names displayed to users to categorize the news item. + + reactions : typing.Optional[typing.Sequence[typing.Optional[str]]] + Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + + newsfeed_assignments : typing.Optional[typing.Sequence[NewsfeedAssignment]] + A list of newsfeed_assignments to assign to the specified newsfeed. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[NewsItem] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "news/news_items", + method="POST", + json={ + "title": title, + "body": body, + "sender_id": sender_id, + "state": state, + "deliver_silently": deliver_silently, + "labels": labels, + "reactions": reactions, + "newsfeed_assignments": convert_and_respect_annotation_metadata( + object_=newsfeed_assignments, annotation=typing.Sequence[NewsfeedAssignment], direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + NewsItem, + construct_type( + type_=NewsItem, # 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 retrieve_news_item( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[NewsItem]: + """ + You can fetch the details of a single news item. + + Parameters + ---------- + id : int + The unique identifier for the news item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[NewsItem] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"news/news_items/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + NewsItem, + construct_type( + type_=NewsItem, # 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 update_news_item( + self, + id: int, + *, + title: str, + sender_id: int, + body: typing.Optional[str] = OMIT, + state: typing.Optional[NewsItemRequestState] = OMIT, + deliver_silently: typing.Optional[bool] = OMIT, + labels: typing.Optional[typing.Sequence[str]] = OMIT, + reactions: typing.Optional[typing.Sequence[typing.Optional[str]]] = OMIT, + newsfeed_assignments: typing.Optional[typing.Sequence[NewsfeedAssignment]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[NewsItem]: + """ + Parameters + ---------- + id : int + The unique identifier for the news item which is given by Intercom. + + title : str + The title of the news item. + + sender_id : int + The id of the sender of the news item. Must be a teammate on the workspace. + + body : typing.Optional[str] + The news item body, which may contain HTML. + + state : typing.Optional[NewsItemRequestState] + News items will not be visible to your users in the assigned newsfeeds until they are set live. + + deliver_silently : typing.Optional[bool] + When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + + labels : typing.Optional[typing.Sequence[str]] + Label names displayed to users to categorize the news item. + + reactions : typing.Optional[typing.Sequence[typing.Optional[str]]] + Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + + newsfeed_assignments : typing.Optional[typing.Sequence[NewsfeedAssignment]] + A list of newsfeed_assignments to assign to the specified newsfeed. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[NewsItem] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"news/news_items/{jsonable_encoder(id)}", + method="PUT", + json={ + "title": title, + "body": body, + "sender_id": sender_id, + "state": state, + "deliver_silently": deliver_silently, + "labels": labels, + "reactions": reactions, + "newsfeed_assignments": convert_and_respect_annotation_metadata( + object_=newsfeed_assignments, annotation=typing.Sequence[NewsfeedAssignment], direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + NewsItem, + construct_type( + type_=NewsItem, # 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 delete_news_item( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DeletedObject]: + """ + You can delete a single news item. + + Parameters + ---------- + id : int + The unique identifier for the news item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeletedObject] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"news/news_items/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeletedObject, + construct_type( + type_=DeletedObject, # 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_live_newsfeed_items( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[PaginatedResponse]: + """ + You can fetch a list of all news items that are live on a given newsfeed + + Parameters + ---------- + id : str + The unique identifier for the news feed item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[PaginatedResponse] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"news/newsfeeds/{jsonable_encoder(id)}/items", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedResponse, + construct_type( + type_=PaginatedResponse, # 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 list_newsfeeds( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[PaginatedResponse]: + """ + You can fetch a list of all newsfeeds + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[PaginatedResponse] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "news/newsfeeds", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedResponse, + construct_type( + type_=PaginatedResponse, # 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 retrieve_newsfeed( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Newsfeed]: + """ + You can fetch the details of a single newsfeed + + Parameters + ---------- + id : str + The unique identifier for the news feed item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Newsfeed] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"news/newsfeeds/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Newsfeed, + construct_type( + type_=Newsfeed, # 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) + + +class AsyncRawNewsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_news_items( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[PaginatedResponse]: + """ + You can fetch a list of all news items + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[PaginatedResponse] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "news/news_items", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedResponse, + construct_type( + type_=PaginatedResponse, # 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 create_news_item( + self, + *, + title: str, + sender_id: int, + body: typing.Optional[str] = OMIT, + state: typing.Optional[NewsItemRequestState] = OMIT, + deliver_silently: typing.Optional[bool] = OMIT, + labels: typing.Optional[typing.Sequence[str]] = OMIT, + reactions: typing.Optional[typing.Sequence[typing.Optional[str]]] = OMIT, + newsfeed_assignments: typing.Optional[typing.Sequence[NewsfeedAssignment]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[NewsItem]: + """ + You can create a news item + + Parameters + ---------- + title : str + The title of the news item. + + sender_id : int + The id of the sender of the news item. Must be a teammate on the workspace. + + body : typing.Optional[str] + The news item body, which may contain HTML. + + state : typing.Optional[NewsItemRequestState] + News items will not be visible to your users in the assigned newsfeeds until they are set live. + + deliver_silently : typing.Optional[bool] + When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + + labels : typing.Optional[typing.Sequence[str]] + Label names displayed to users to categorize the news item. + + reactions : typing.Optional[typing.Sequence[typing.Optional[str]]] + Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + + newsfeed_assignments : typing.Optional[typing.Sequence[NewsfeedAssignment]] + A list of newsfeed_assignments to assign to the specified newsfeed. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[NewsItem] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "news/news_items", + method="POST", + json={ + "title": title, + "body": body, + "sender_id": sender_id, + "state": state, + "deliver_silently": deliver_silently, + "labels": labels, + "reactions": reactions, + "newsfeed_assignments": convert_and_respect_annotation_metadata( + object_=newsfeed_assignments, annotation=typing.Sequence[NewsfeedAssignment], direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + NewsItem, + construct_type( + type_=NewsItem, # 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 retrieve_news_item( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[NewsItem]: + """ + You can fetch the details of a single news item. + + Parameters + ---------- + id : int + The unique identifier for the news item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[NewsItem] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"news/news_items/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + NewsItem, + construct_type( + type_=NewsItem, # 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 update_news_item( + self, + id: int, + *, + title: str, + sender_id: int, + body: typing.Optional[str] = OMIT, + state: typing.Optional[NewsItemRequestState] = OMIT, + deliver_silently: typing.Optional[bool] = OMIT, + labels: typing.Optional[typing.Sequence[str]] = OMIT, + reactions: typing.Optional[typing.Sequence[typing.Optional[str]]] = OMIT, + newsfeed_assignments: typing.Optional[typing.Sequence[NewsfeedAssignment]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[NewsItem]: + """ + Parameters + ---------- + id : int + The unique identifier for the news item which is given by Intercom. + + title : str + The title of the news item. + + sender_id : int + The id of the sender of the news item. Must be a teammate on the workspace. + + body : typing.Optional[str] + The news item body, which may contain HTML. + + state : typing.Optional[NewsItemRequestState] + News items will not be visible to your users in the assigned newsfeeds until they are set live. + + deliver_silently : typing.Optional[bool] + When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + + labels : typing.Optional[typing.Sequence[str]] + Label names displayed to users to categorize the news item. + + reactions : typing.Optional[typing.Sequence[typing.Optional[str]]] + Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + + newsfeed_assignments : typing.Optional[typing.Sequence[NewsfeedAssignment]] + A list of newsfeed_assignments to assign to the specified newsfeed. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[NewsItem] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"news/news_items/{jsonable_encoder(id)}", + method="PUT", + json={ + "title": title, + "body": body, + "sender_id": sender_id, + "state": state, + "deliver_silently": deliver_silently, + "labels": labels, + "reactions": reactions, + "newsfeed_assignments": convert_and_respect_annotation_metadata( + object_=newsfeed_assignments, annotation=typing.Sequence[NewsfeedAssignment], direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + NewsItem, + construct_type( + type_=NewsItem, # 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 delete_news_item( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DeletedObject]: + """ + You can delete a single news item. + + Parameters + ---------- + id : int + The unique identifier for the news item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeletedObject] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"news/news_items/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeletedObject, + construct_type( + type_=DeletedObject, # 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_live_newsfeed_items( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[PaginatedResponse]: + """ + You can fetch a list of all news items that are live on a given newsfeed + + Parameters + ---------- + id : str + The unique identifier for the news feed item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[PaginatedResponse] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"news/newsfeeds/{jsonable_encoder(id)}/items", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedResponse, + construct_type( + type_=PaginatedResponse, # 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 list_newsfeeds( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[PaginatedResponse]: + """ + You can fetch a list of all newsfeeds + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[PaginatedResponse] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "news/newsfeeds", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + PaginatedResponse, + construct_type( + type_=PaginatedResponse, # 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 retrieve_newsfeed( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Newsfeed]: + """ + You can fetch the details of a single newsfeed + + Parameters + ---------- + id : str + The unique identifier for the news feed item which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Newsfeed] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"news/newsfeeds/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Newsfeed, + construct_type( + type_=Newsfeed, # 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) diff --git a/src/intercom/unstable/news/types/__init__.py b/src/intercom/unstable/news/types/__init__.py new file mode 100644 index 00000000..1af0e8e4 --- /dev/null +++ b/src/intercom/unstable/news/types/__init__.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .news_item import NewsItem + from .news_item_state import NewsItemState + from .newsfeed import Newsfeed + from .newsfeed_assignment import NewsfeedAssignment +_dynamic_imports: typing.Dict[str, str] = { + "NewsItem": ".news_item", + "NewsItemState": ".news_item_state", + "Newsfeed": ".newsfeed", + "NewsfeedAssignment": ".newsfeed_assignment", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["NewsItem", "NewsItemState", "Newsfeed", "NewsfeedAssignment"] diff --git a/src/intercom/unstable/news/types/news_item.py b/src/intercom/unstable/news/types/news_item.py new file mode 100644 index 00000000..653c87c2 --- /dev/null +++ b/src/intercom/unstable/news/types/news_item.py @@ -0,0 +1,94 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .news_item_state import NewsItemState +from .newsfeed_assignment import NewsfeedAssignment + + +class NewsItem(UncheckedBaseModel): + """ + A News Item is a content type in Intercom enabling you to announce product updates, company news, promotions, events and more with your customers. + """ + + type: typing.Optional[typing.Literal["news-item"]] = pydantic.Field(default=None) + """ + The type of object. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the news item which is given by Intercom. + """ + + workspace_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the workspace which the news item belongs to. + """ + + title: typing.Optional[str] = pydantic.Field(default=None) + """ + The title of the news item. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The news item body, which may contain HTML. + """ + + sender_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of the sender of the news item. Must be a teammate on the workspace. + """ + + state: typing.Optional[NewsItemState] = pydantic.Field(default=None) + """ + News items will not be visible to your users in the assigned newsfeeds until they are set live. + """ + + newsfeed_assignments: typing.Optional[typing.List[NewsfeedAssignment]] = pydantic.Field(default=None) + """ + A list of newsfeed_assignments to assign to the specified newsfeed. + """ + + labels: typing.Optional[typing.List[typing.Optional[str]]] = pydantic.Field(default=None) + """ + Label names displayed to users to categorize the news item. + """ + + cover_image_url: typing.Optional[str] = pydantic.Field(default=None) + """ + URL of the image used as cover. Must have .jpg or .png extension. + """ + + reactions: typing.Optional[typing.List[typing.Optional[str]]] = pydantic.Field(default=None) + """ + Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + """ + + deliver_silently: typing.Optional[bool] = pydantic.Field(default=None) + """ + When set to true, the news item will appear in the messenger newsfeed without showing a notification badge. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Timestamp for when the news item was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Timestamp for when the news item was last updated. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/news/types/news_item_state.py b/src/intercom/unstable/news/types/news_item_state.py new file mode 100644 index 00000000..2b16536c --- /dev/null +++ b/src/intercom/unstable/news/types/news_item_state.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +NewsItemState = typing.Union[typing.Literal["draft", "live"], typing.Any] diff --git a/src/intercom/unstable/news/types/newsfeed.py b/src/intercom/unstable/news/types/newsfeed.py new file mode 100644 index 00000000..b1c012ef --- /dev/null +++ b/src/intercom/unstable/news/types/newsfeed.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class Newsfeed(UncheckedBaseModel): + """ + A newsfeed is a collection of news items, targeted to a specific audience. + + Newsfeeds currently cannot be edited through the API, please refer to [this article](https://www.intercom.com/help/en/articles/6362267-getting-started-with-news) to set up your newsfeeds in Intercom. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the newsfeed which is given by Intercom. + """ + + type: typing.Optional[typing.Literal["newsfeed"]] = pydantic.Field(default=None) + """ + The type of object. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the newsfeed. This name will never be visible to your users. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Timestamp for when the newsfeed was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Timestamp for when the newsfeed was last updated. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/news/types/newsfeed_assignment.py b/src/intercom/unstable/news/types/newsfeed_assignment.py new file mode 100644 index 00000000..124dc6a3 --- /dev/null +++ b/src/intercom/unstable/news/types/newsfeed_assignment.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class NewsfeedAssignment(UncheckedBaseModel): + """ + Assigns a news item to a newsfeed. + """ + + newsfeed_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The unique identifier for the newsfeed which is given by Intercom. Publish dates cannot be in the future, to schedule news items use the dedicated feature in app (see this article). + """ + + published_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Publish date of the news item on the newsfeed, use this field if you want to set a publish date in the past (e.g. when importing existing news items). On write, this field will be ignored if the news item state is "draft". + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/notes/__init__.py b/src/intercom/unstable/notes/__init__.py new file mode 100644 index 00000000..b166d0d0 --- /dev/null +++ b/src/intercom/unstable/notes/__init__.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import CompanyNote, CompanyNoteCompany, Note, NoteContact +_dynamic_imports: typing.Dict[str, str] = { + "CompanyNote": ".types", + "CompanyNoteCompany": ".types", + "Note": ".types", + "NoteContact": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["CompanyNote", "CompanyNoteCompany", "Note", "NoteContact"] diff --git a/src/intercom/unstable/notes/client.py b/src/intercom/unstable/notes/client.py new file mode 100644 index 00000000..4b19ef0e --- /dev/null +++ b/src/intercom/unstable/notes/client.py @@ -0,0 +1,368 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..types.note_list import NoteList +from .raw_client import AsyncRawNotesClient, RawNotesClient +from .types.note import Note + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class NotesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawNotesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawNotesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawNotesClient + """ + return self._raw_client + + def list_company_notes(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> NoteList: + """ + You can fetch a list of notes that are associated to a company. + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + NoteList + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.notes.list_company_notes( + id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + ) + """ + _response = self._raw_client.list_company_notes(id, request_options=request_options) + return _response.data + + def list_notes(self, id: int, *, request_options: typing.Optional[RequestOptions] = None) -> NoteList: + """ + You can fetch a list of notes that are associated to a contact. + + Parameters + ---------- + id : int + The unique identifier of a contact. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + NoteList + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.notes.list_notes( + id=1, + ) + """ + _response = self._raw_client.list_notes(id, request_options=request_options) + return _response.data + + def create_note( + self, + id: int, + *, + body: str, + contact_id: typing.Optional[str] = OMIT, + admin_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Note: + """ + You can add a note to a single contact. + + Parameters + ---------- + id : int + The unique identifier of a given contact. + + body : str + The text of the note. + + contact_id : typing.Optional[str] + The unique identifier of a given contact. + + admin_id : typing.Optional[str] + The unique identifier of a given admin. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Note + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.notes.create_note( + id=1, + body="Hello", + contact_id="6762f0ad1bb69f9f2193bb62", + admin_id="123", + ) + """ + _response = self._raw_client.create_note( + id, body=body, contact_id=contact_id, admin_id=admin_id, request_options=request_options + ) + return _response.data + + def retrieve_note(self, id: int, *, request_options: typing.Optional[RequestOptions] = None) -> Note: + """ + You can fetch the details of a single note. + + Parameters + ---------- + id : int + The unique identifier of a given note + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Note + Note found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.notes.retrieve_note( + id=1, + ) + """ + _response = self._raw_client.retrieve_note(id, request_options=request_options) + return _response.data + + +class AsyncNotesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawNotesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawNotesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawNotesClient + """ + return self._raw_client + + async def list_company_notes(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> NoteList: + """ + You can fetch a list of notes that are associated to a company. + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + NoteList + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.notes.list_company_notes( + id="5f4d3c1c-7b1b-4d7d-a97e-6095715c6632", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_company_notes(id, request_options=request_options) + return _response.data + + async def list_notes(self, id: int, *, request_options: typing.Optional[RequestOptions] = None) -> NoteList: + """ + You can fetch a list of notes that are associated to a contact. + + Parameters + ---------- + id : int + The unique identifier of a contact. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + NoteList + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.notes.list_notes( + id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_notes(id, request_options=request_options) + return _response.data + + async def create_note( + self, + id: int, + *, + body: str, + contact_id: typing.Optional[str] = OMIT, + admin_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Note: + """ + You can add a note to a single contact. + + Parameters + ---------- + id : int + The unique identifier of a given contact. + + body : str + The text of the note. + + contact_id : typing.Optional[str] + The unique identifier of a given contact. + + admin_id : typing.Optional[str] + The unique identifier of a given admin. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Note + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.notes.create_note( + id=1, + body="Hello", + contact_id="6762f0ad1bb69f9f2193bb62", + admin_id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_note( + id, body=body, contact_id=contact_id, admin_id=admin_id, request_options=request_options + ) + return _response.data + + async def retrieve_note(self, id: int, *, request_options: typing.Optional[RequestOptions] = None) -> Note: + """ + You can fetch the details of a single note. + + Parameters + ---------- + id : int + The unique identifier of a given note + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Note + Note found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.notes.retrieve_note( + id=1, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.retrieve_note(id, request_options=request_options) + return _response.data diff --git a/src/intercom/unstable/notes/raw_client.py b/src/intercom/unstable/notes/raw_client.py new file mode 100644 index 00000000..65fb6e93 --- /dev/null +++ b/src/intercom/unstable/notes/raw_client.py @@ -0,0 +1,495 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from ..types.note_list import NoteList +from .types.note import Note + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawNotesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_company_notes( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[NoteList]: + """ + You can fetch a list of notes that are associated to a company. + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[NoteList] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(id)}/notes", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + NoteList, + construct_type( + type_=NoteList, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + 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_notes(self, id: int, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[NoteList]: + """ + You can fetch a list of notes that are associated to a contact. + + Parameters + ---------- + id : int + The unique identifier of a contact. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[NoteList] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(id)}/notes", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + NoteList, + construct_type( + type_=NoteList, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + 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 create_note( + self, + id: int, + *, + body: str, + contact_id: typing.Optional[str] = OMIT, + admin_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Note]: + """ + You can add a note to a single contact. + + Parameters + ---------- + id : int + The unique identifier of a given contact. + + body : str + The text of the note. + + contact_id : typing.Optional[str] + The unique identifier of a given contact. + + admin_id : typing.Optional[str] + The unique identifier of a given admin. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Note] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(id)}/notes", + method="POST", + json={ + "body": body, + "contact_id": contact_id, + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Note, + construct_type( + type_=Note, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + 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 retrieve_note(self, id: int, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Note]: + """ + You can fetch the details of a single note. + + Parameters + ---------- + id : int + The unique identifier of a given note + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Note] + Note found + """ + _response = self._client_wrapper.httpx_client.request( + f"notes/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Note, + construct_type( + type_=Note, # 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) + + +class AsyncRawNotesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_company_notes( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[NoteList]: + """ + You can fetch a list of notes that are associated to a company. + + Parameters + ---------- + id : str + The unique identifier for the company which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[NoteList] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + f"companies/{jsonable_encoder(id)}/notes", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + NoteList, + construct_type( + type_=NoteList, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + 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_notes( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[NoteList]: + """ + You can fetch a list of notes that are associated to a contact. + + Parameters + ---------- + id : int + The unique identifier of a contact. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[NoteList] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(id)}/notes", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + NoteList, + construct_type( + type_=NoteList, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + 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 create_note( + self, + id: int, + *, + body: str, + contact_id: typing.Optional[str] = OMIT, + admin_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Note]: + """ + You can add a note to a single contact. + + Parameters + ---------- + id : int + The unique identifier of a given contact. + + body : str + The text of the note. + + contact_id : typing.Optional[str] + The unique identifier of a given contact. + + admin_id : typing.Optional[str] + The unique identifier of a given admin. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Note] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(id)}/notes", + method="POST", + json={ + "body": body, + "contact_id": contact_id, + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Note, + construct_type( + type_=Note, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + 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 retrieve_note( + self, id: int, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Note]: + """ + You can fetch the details of a single note. + + Parameters + ---------- + id : int + The unique identifier of a given note + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Note] + Note found + """ + _response = await self._client_wrapper.httpx_client.request( + f"notes/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Note, + construct_type( + type_=Note, # 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) diff --git a/src/intercom/unstable/notes/types/__init__.py b/src/intercom/unstable/notes/types/__init__.py new file mode 100644 index 00000000..b5e03c94 --- /dev/null +++ b/src/intercom/unstable/notes/types/__init__.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .company_note import CompanyNote + from .company_note_company import CompanyNoteCompany + from .note import Note + from .note_contact import NoteContact +_dynamic_imports: typing.Dict[str, str] = { + "CompanyNote": ".company_note", + "CompanyNoteCompany": ".company_note_company", + "Note": ".note", + "NoteContact": ".note_contact", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["CompanyNote", "CompanyNoteCompany", "Note", "NoteContact"] diff --git a/src/intercom/unstable/notes/types/company_note.py b/src/intercom/unstable/notes/types/company_note.py new file mode 100644 index 00000000..18694d7c --- /dev/null +++ b/src/intercom/unstable/notes/types/company_note.py @@ -0,0 +1,54 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...admins.types.admin import Admin +from .company_note_company import CompanyNoteCompany + + +class CompanyNote(UncheckedBaseModel): + """ + Notes allow you to annotate and comment on companies. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `note`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the note. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the note was created. + """ + + company: typing.Optional[CompanyNoteCompany] = pydantic.Field(default=None) + """ + Represents the company that the note was created about. + """ + + author: typing.Optional[Admin] = pydantic.Field(default=None) + """ + Optional. Represents the Admin that created the note. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The body text of the note. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/notes/types/company_note_company.py b/src/intercom/unstable/notes/types/company_note_company.py new file mode 100644 index 00000000..90dd8352 --- /dev/null +++ b/src/intercom/unstable/notes/types/company_note_company.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class CompanyNoteCompany(UncheckedBaseModel): + """ + Represents the company that the note was created about. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `company`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the company. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/notes/types/note.py b/src/intercom/unstable/notes/types/note.py new file mode 100644 index 00000000..db3abe71 --- /dev/null +++ b/src/intercom/unstable/notes/types/note.py @@ -0,0 +1,54 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...admins.types.admin import Admin +from .note_contact import NoteContact + + +class Note(UncheckedBaseModel): + """ + Notes allow you to annotate and comment on your contacts. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `note`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the note. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the note was created. + """ + + contact: typing.Optional[NoteContact] = pydantic.Field(default=None) + """ + Represents the contact that the note was created about. + """ + + author: typing.Optional[Admin] = pydantic.Field(default=None) + """ + Optional. Represents the Admin that created the note. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The body text of the note. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/notes/types/note_contact.py b/src/intercom/unstable/notes/types/note_contact.py new file mode 100644 index 00000000..8117c1dd --- /dev/null +++ b/src/intercom/unstable/notes/types/note_contact.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class NoteContact(UncheckedBaseModel): + """ + Represents the contact that the note was created about. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `contact`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the contact. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/raw_client.py b/src/intercom/unstable/raw_client.py new file mode 100644 index 00000000..5201a511 --- /dev/null +++ b/src/intercom/unstable/raw_client.py @@ -0,0 +1,13 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper + + +class RawUnstableClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + +class AsyncRawUnstableClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper diff --git a/src/intercom/unstable/segments/__init__.py b/src/intercom/unstable/segments/__init__.py new file mode 100644 index 00000000..d5f7ec59 --- /dev/null +++ b/src/intercom/unstable/segments/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import Segment, SegmentPersonType +_dynamic_imports: typing.Dict[str, str] = {"Segment": ".types", "SegmentPersonType": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Segment", "SegmentPersonType"] diff --git a/src/intercom/unstable/segments/client.py b/src/intercom/unstable/segments/client.py new file mode 100644 index 00000000..e413cac2 --- /dev/null +++ b/src/intercom/unstable/segments/client.py @@ -0,0 +1,185 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..types.segment_list import SegmentList +from .raw_client import AsyncRawSegmentsClient, RawSegmentsClient +from .types.segment import Segment + + +class SegmentsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawSegmentsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawSegmentsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawSegmentsClient + """ + return self._raw_client + + def list_segments( + self, *, include_count: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None + ) -> SegmentList: + """ + You can fetch a list of all segments. + + Parameters + ---------- + include_count : typing.Optional[bool] + It includes the count of contacts that belong to each segment. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SegmentList + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.segments.list_segments( + include_count=True, + ) + """ + _response = self._raw_client.list_segments(include_count=include_count, request_options=request_options) + return _response.data + + def retrieve_segment(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Segment: + """ + You can fetch the details of a single segment. + + Parameters + ---------- + id : str + The unique identified of a given segment. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Segment + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.segments.retrieve_segment( + id="123", + ) + """ + _response = self._raw_client.retrieve_segment(id, request_options=request_options) + return _response.data + + +class AsyncSegmentsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawSegmentsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawSegmentsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawSegmentsClient + """ + return self._raw_client + + async def list_segments( + self, *, include_count: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None + ) -> SegmentList: + """ + You can fetch a list of all segments. + + Parameters + ---------- + include_count : typing.Optional[bool] + It includes the count of contacts that belong to each segment. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SegmentList + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.segments.list_segments( + include_count=True, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_segments(include_count=include_count, request_options=request_options) + return _response.data + + async def retrieve_segment(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Segment: + """ + You can fetch the details of a single segment. + + Parameters + ---------- + id : str + The unique identified of a given segment. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Segment + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.segments.retrieve_segment( + id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.retrieve_segment(id, request_options=request_options) + return _response.data diff --git a/src/intercom/unstable/segments/raw_client.py b/src/intercom/unstable/segments/raw_client.py new file mode 100644 index 00000000..2350d846 --- /dev/null +++ b/src/intercom/unstable/segments/raw_client.py @@ -0,0 +1,254 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from ..types.segment_list import SegmentList +from .types.segment import Segment + + +class RawSegmentsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_segments( + self, *, include_count: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[SegmentList]: + """ + You can fetch a list of all segments. + + Parameters + ---------- + include_count : typing.Optional[bool] + It includes the count of contacts that belong to each segment. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[SegmentList] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "segments", + method="GET", + params={ + "include_count": include_count, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SegmentList, + construct_type( + type_=SegmentList, # 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 retrieve_segment( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Segment]: + """ + You can fetch the details of a single segment. + + Parameters + ---------- + id : str + The unique identified of a given segment. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Segment] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + f"segments/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Segment, + construct_type( + type_=Segment, # 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) + + +class AsyncRawSegmentsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_segments( + self, *, include_count: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[SegmentList]: + """ + You can fetch a list of all segments. + + Parameters + ---------- + include_count : typing.Optional[bool] + It includes the count of contacts that belong to each segment. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[SegmentList] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "segments", + method="GET", + params={ + "include_count": include_count, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + SegmentList, + construct_type( + type_=SegmentList, # 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 retrieve_segment( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Segment]: + """ + You can fetch the details of a single segment. + + Parameters + ---------- + id : str + The unique identified of a given segment. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Segment] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + f"segments/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Segment, + construct_type( + type_=Segment, # 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) diff --git a/src/intercom/unstable/segments/types/__init__.py b/src/intercom/unstable/segments/types/__init__.py new file mode 100644 index 00000000..c6ec8262 --- /dev/null +++ b/src/intercom/unstable/segments/types/__init__.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .segment import Segment + from .segment_person_type import SegmentPersonType +_dynamic_imports: typing.Dict[str, str] = {"Segment": ".segment", "SegmentPersonType": ".segment_person_type"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Segment", "SegmentPersonType"] diff --git a/src/intercom/unstable/segments/types/segment.py b/src/intercom/unstable/segments/types/segment.py new file mode 100644 index 00000000..ce873d4a --- /dev/null +++ b/src/intercom/unstable/segments/types/segment.py @@ -0,0 +1,58 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .segment_person_type import SegmentPersonType + + +class Segment(UncheckedBaseModel): + """ + A segment is a group of your contacts defined by the rules that you set. + """ + + type: typing.Optional[typing.Literal["segment"]] = pydantic.Field(default=None) + """ + The type of object. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier representing the segment. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the segment. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the segment was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the segment was updated. + """ + + person_type: typing.Optional[SegmentPersonType] = pydantic.Field(default=None) + """ + Type of the contact: contact (lead) or user. + """ + + count: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of items in the user segment. It's returned when `include_count=true` is included in the request. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/segments/types/segment_person_type.py b/src/intercom/unstable/segments/types/segment_person_type.py new file mode 100644 index 00000000..3089d52f --- /dev/null +++ b/src/intercom/unstable/segments/types/segment_person_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +SegmentPersonType = typing.Union[typing.Literal["contact", "user"], typing.Any] diff --git a/src/intercom/unstable/subscription_types/__init__.py b/src/intercom/unstable/subscription_types/__init__.py new file mode 100644 index 00000000..306f22bf --- /dev/null +++ b/src/intercom/unstable/subscription_types/__init__.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + SubscriptionType, + SubscriptionTypeConsentType, + SubscriptionTypeContentTypesItem, + SubscriptionTypeState, + ) +_dynamic_imports: typing.Dict[str, str] = { + "SubscriptionType": ".types", + "SubscriptionTypeConsentType": ".types", + "SubscriptionTypeContentTypesItem": ".types", + "SubscriptionTypeState": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "SubscriptionType", + "SubscriptionTypeConsentType", + "SubscriptionTypeContentTypesItem", + "SubscriptionTypeState", +] diff --git a/src/intercom/unstable/subscription_types/client.py b/src/intercom/unstable/subscription_types/client.py new file mode 100644 index 00000000..bb9913be --- /dev/null +++ b/src/intercom/unstable/subscription_types/client.py @@ -0,0 +1,300 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..types.subscription_type_list import SubscriptionTypeList +from .raw_client import AsyncRawSubscriptionTypesClient, RawSubscriptionTypesClient +from .types.subscription_type import SubscriptionType + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class SubscriptionTypesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawSubscriptionTypesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawSubscriptionTypesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawSubscriptionTypesClient + """ + return self._raw_client + + def attach_subscription_type_to_contact( + self, contact_id: str, *, id: str, consent_type: str, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + + 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 + ------- + SubscriptionType + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.subscription_types.attach_subscription_type_to_contact( + contact_id="63a07ddf05a32042dffac965", + id="37846", + consent_type="opt_in", + ) + """ + _response = self._raw_client.attach_subscription_type_to_contact( + contact_id, id=id, consent_type=consent_type, request_options=request_options + ) + return _response.data + + def detach_subscription_type_to_contact( + self, contact_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + + id : str + The unique identifier for the subscription type which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SubscriptionType + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.subscription_types.detach_subscription_type_to_contact( + contact_id="63a07ddf05a32042dffac965", + id="37846", + ) + """ + _response = self._raw_client.detach_subscription_type_to_contact( + contact_id, id, request_options=request_options + ) + return _response.data + + def list_subscription_types( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> SubscriptionTypeList: + """ + You can list all subscription types. A list of subscription type objects will be returned. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SubscriptionTypeList + Successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.subscription_types.list_subscription_types() + """ + _response = self._raw_client.list_subscription_types(request_options=request_options) + return _response.data + + +class AsyncSubscriptionTypesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawSubscriptionTypesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawSubscriptionTypesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawSubscriptionTypesClient + """ + return self._raw_client + + async def attach_subscription_type_to_contact( + self, contact_id: str, *, id: str, consent_type: str, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + + 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 + ------- + SubscriptionType + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.subscription_types.attach_subscription_type_to_contact( + contact_id="63a07ddf05a32042dffac965", + id="37846", + consent_type="opt_in", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.attach_subscription_type_to_contact( + contact_id, id=id, consent_type=consent_type, request_options=request_options + ) + return _response.data + + async def detach_subscription_type_to_contact( + self, contact_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> 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 + + id : str + The unique identifier for the subscription type which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SubscriptionType + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.subscription_types.detach_subscription_type_to_contact( + contact_id="63a07ddf05a32042dffac965", + id="37846", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.detach_subscription_type_to_contact( + contact_id, id, request_options=request_options + ) + return _response.data + + async def list_subscription_types( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> SubscriptionTypeList: + """ + You can list all subscription types. A list of subscription type objects will be returned. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SubscriptionTypeList + Successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.subscription_types.list_subscription_types() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_subscription_types(request_options=request_options) + return _response.data diff --git a/src/intercom/unstable/subscription_types/raw_client.py b/src/intercom/unstable/subscription_types/raw_client.py new file mode 100644 index 00000000..53edd4ad --- /dev/null +++ b/src/intercom/unstable/subscription_types/raw_client.py @@ -0,0 +1,413 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from ..types.subscription_type_list import SubscriptionTypeList +from .types.subscription_type import SubscriptionType + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawSubscriptionTypesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def attach_subscription_type_to_contact( + self, contact_id: str, *, 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 + + 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": 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_type_to_contact( + self, contact_id: str, 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 + + 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(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_subscription_types( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[SubscriptionTypeList]: + """ + You can list all subscription types. A list of subscription type objects will be returned. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[SubscriptionTypeList] + Successful + """ + _response = self._client_wrapper.httpx_client.request( + "subscription_types", + 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(), + ), + ), + ) + _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 AsyncRawSubscriptionTypesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def attach_subscription_type_to_contact( + self, contact_id: str, *, 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 + + 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": 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_type_to_contact( + self, contact_id: str, 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 + + 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(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_subscription_types( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[SubscriptionTypeList]: + """ + You can list all subscription types. A list of subscription type objects will be returned. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[SubscriptionTypeList] + Successful + """ + _response = await self._client_wrapper.httpx_client.request( + "subscription_types", + 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(), + ), + ), + ) + _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) diff --git a/src/intercom/unstable/subscription_types/types/__init__.py b/src/intercom/unstable/subscription_types/types/__init__.py new file mode 100644 index 00000000..dfc1b4cb --- /dev/null +++ b/src/intercom/unstable/subscription_types/types/__init__.py @@ -0,0 +1,47 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .subscription_type import SubscriptionType + from .subscription_type_consent_type import SubscriptionTypeConsentType + from .subscription_type_content_types_item import SubscriptionTypeContentTypesItem + from .subscription_type_state import SubscriptionTypeState +_dynamic_imports: typing.Dict[str, str] = { + "SubscriptionType": ".subscription_type", + "SubscriptionTypeConsentType": ".subscription_type_consent_type", + "SubscriptionTypeContentTypesItem": ".subscription_type_content_types_item", + "SubscriptionTypeState": ".subscription_type_state", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "SubscriptionType", + "SubscriptionTypeConsentType", + "SubscriptionTypeContentTypesItem", + "SubscriptionTypeState", +] diff --git a/src/intercom/unstable/subscription_types/types/subscription_type.py b/src/intercom/unstable/subscription_types/types/subscription_type.py new file mode 100644 index 00000000..005c71fb --- /dev/null +++ b/src/intercom/unstable/subscription_types/types/subscription_type.py @@ -0,0 +1,57 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...types.translation import Translation +from .subscription_type_consent_type import SubscriptionTypeConsentType +from .subscription_type_content_types_item import SubscriptionTypeContentTypesItem +from .subscription_type_state import SubscriptionTypeState + + +class SubscriptionType(UncheckedBaseModel): + """ + A subscription type lets customers easily opt out of non-essential communications without missing what's important to them. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of the object - subscription + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier representing the subscription type. + """ + + state: typing.Optional[SubscriptionTypeState] = pydantic.Field(default=None) + """ + The state of the subscription type. + """ + + default_translation: typing.Optional[Translation] = None + translations: typing.Optional[typing.List[Translation]] = pydantic.Field(default=None) + """ + An array of translations objects with the localised version of the subscription type in each available locale within your translation settings. + """ + + consent_type: typing.Optional[SubscriptionTypeConsentType] = pydantic.Field(default=None) + """ + Describes the type of consent. + """ + + content_types: typing.Optional[typing.List[SubscriptionTypeContentTypesItem]] = pydantic.Field(default=None) + """ + The message types that this subscription supports - can contain `email` or `sms_message`. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/subscription_types/types/subscription_type_consent_type.py b/src/intercom/unstable/subscription_types/types/subscription_type_consent_type.py new file mode 100644 index 00000000..d7f8b6a9 --- /dev/null +++ b/src/intercom/unstable/subscription_types/types/subscription_type_consent_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +SubscriptionTypeConsentType = typing.Union[typing.Literal["opt_out", "opt_in"], typing.Any] diff --git a/src/intercom/unstable/subscription_types/types/subscription_type_content_types_item.py b/src/intercom/unstable/subscription_types/types/subscription_type_content_types_item.py new file mode 100644 index 00000000..d895f703 --- /dev/null +++ b/src/intercom/unstable/subscription_types/types/subscription_type_content_types_item.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +SubscriptionTypeContentTypesItem = typing.Union[typing.Literal["email", "sms_message"], typing.Any] diff --git a/src/intercom/unstable/subscription_types/types/subscription_type_state.py b/src/intercom/unstable/subscription_types/types/subscription_type_state.py new file mode 100644 index 00000000..65c92596 --- /dev/null +++ b/src/intercom/unstable/subscription_types/types/subscription_type_state.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +SubscriptionTypeState = typing.Union[typing.Literal["live", "draft", "archived"], typing.Any] diff --git a/src/intercom/unstable/switch/__init__.py b/src/intercom/unstable/switch/__init__.py new file mode 100644 index 00000000..5cde0202 --- /dev/null +++ b/src/intercom/unstable/switch/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/src/intercom/unstable/switch/client.py b/src/intercom/unstable/switch/client.py new file mode 100644 index 00000000..2f86f8e5 --- /dev/null +++ b/src/intercom/unstable/switch/client.py @@ -0,0 +1,121 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..types.phone_switch import PhoneSwitch +from .raw_client import AsyncRawSwitchClient, RawSwitchClient + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class SwitchClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawSwitchClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawSwitchClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawSwitchClient + """ + return self._raw_client + + def create_phone_switch( + self, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[PhoneSwitch]: + """ + You can use the API to deflect phone calls to the Intercom Messenger. + Calling this endpoint will send an SMS with a link to the Messenger to the phone number specified. + + If custom attributes are specified, they will be added to the user or lead's custom data attributes. + + Parameters + ---------- + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[PhoneSwitch] + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.switch.create_phone_switch( + request={"key": "value"}, + ) + """ + _response = self._raw_client.create_phone_switch(request=request, request_options=request_options) + return _response.data + + +class AsyncSwitchClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawSwitchClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawSwitchClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawSwitchClient + """ + return self._raw_client + + async def create_phone_switch( + self, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[PhoneSwitch]: + """ + You can use the API to deflect phone calls to the Intercom Messenger. + Calling this endpoint will send an SMS with a link to the Messenger to the phone number specified. + + If custom attributes are specified, they will be added to the user or lead's custom data attributes. + + Parameters + ---------- + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[PhoneSwitch] + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.switch.create_phone_switch( + request={"key": "value"}, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_phone_switch(request=request, request_options=request_options) + return _response.data diff --git a/src/intercom/unstable/switch/raw_client.py b/src/intercom/unstable/switch/raw_client.py new file mode 100644 index 00000000..95a8f911 --- /dev/null +++ b/src/intercom/unstable/switch/raw_client.py @@ -0,0 +1,190 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.http_response import AsyncHttpResponse, HttpResponse +from ...core.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.bad_request_error import BadRequestError +from ..errors.unauthorized_error import UnauthorizedError +from ..errors.unprocessable_entity_error import UnprocessableEntityError +from ..types.error import Error +from ..types.phone_switch import PhoneSwitch + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawSwitchClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def create_phone_switch( + self, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[typing.Optional[PhoneSwitch]]: + """ + You can use the API to deflect phone calls to the Intercom Messenger. + Calling this endpoint will send an SMS with a link to the Messenger to the phone number specified. + + If custom attributes are specified, they will be added to the user or lead's custom data attributes. + + Parameters + ---------- + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[PhoneSwitch]] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "phone_call_redirects", + method="POST", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[PhoneSwitch], + construct_type( + type_=typing.Optional[PhoneSwitch], # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 == 422: + raise UnprocessableEntityError( + 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) + + +class AsyncRawSwitchClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def create_phone_switch( + self, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[typing.Optional[PhoneSwitch]]: + """ + You can use the API to deflect phone calls to the Intercom Messenger. + Calling this endpoint will send an SMS with a link to the Messenger to the phone number specified. + + If custom attributes are specified, they will be added to the user or lead's custom data attributes. + + Parameters + ---------- + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[PhoneSwitch]] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "phone_call_redirects", + method="POST", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[PhoneSwitch], + construct_type( + type_=typing.Optional[PhoneSwitch], # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 == 422: + raise UnprocessableEntityError( + 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) diff --git a/src/intercom/unstable/tags/__init__.py b/src/intercom/unstable/tags/__init__.py new file mode 100644 index 00000000..b341ad78 --- /dev/null +++ b/src/intercom/unstable/tags/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import CreateTagRequestBody, Tag, TagBasic +_dynamic_imports: typing.Dict[str, str] = {"CreateTagRequestBody": ".types", "Tag": ".types", "TagBasic": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["CreateTagRequestBody", "Tag", "TagBasic"] diff --git a/src/intercom/unstable/tags/client.py b/src/intercom/unstable/tags/client.py new file mode 100644 index 00000000..30f7d78a --- /dev/null +++ b/src/intercom/unstable/tags/client.py @@ -0,0 +1,887 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..types.tag_list import TagList +from .raw_client import AsyncRawTagsClient, RawTagsClient +from .types.create_tag_request_body import CreateTagRequestBody +from .types.tag import Tag + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class TagsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawTagsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawTagsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawTagsClient + """ + return self._raw_client + + def attach_tag_to_contact( + self, contact_id: str, *, id: str, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can tag a specific contact. This will return a tag object for the tag that was added to the contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + id : str + The unique identifier for the tag which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.tags.attach_tag_to_contact( + contact_id="63a07ddf05a32042dffac965", + id="7522907", + ) + """ + _response = self._raw_client.attach_tag_to_contact(contact_id, id=id, request_options=request_options) + return _response.data + + def detach_tag_from_contact( + self, contact_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can remove tag from a specific contact. This will return a tag object for the tag that was removed from the contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + id : str + The unique identifier for the tag which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.tags.detach_tag_from_contact( + contact_id="63a07ddf05a32042dffac965", + id="7522907", + ) + """ + _response = self._raw_client.detach_tag_from_contact(contact_id, id, request_options=request_options) + return _response.data + + def attach_tag_to_conversation( + self, conversation_id: str, *, id: str, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can tag a specific conversation. This will return a tag object for the tag that was added to the conversation. + + Parameters + ---------- + conversation_id : str + conversation_id + + id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.tags.attach_tag_to_conversation( + conversation_id="64619700005694", + id="7522907", + admin_id="780", + ) + """ + _response = self._raw_client.attach_tag_to_conversation( + conversation_id, id=id, admin_id=admin_id, request_options=request_options + ) + return _response.data + + def detach_tag_from_conversation( + self, conversation_id: str, id: str, *, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can remove tag from a specific conversation. This will return a tag object for the tag that was removed from the conversation. + + Parameters + ---------- + conversation_id : str + conversation_id + + id : str + id + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.tags.detach_tag_from_conversation( + conversation_id="64619700005694", + id="7522907", + admin_id="123", + ) + """ + _response = self._raw_client.detach_tag_from_conversation( + conversation_id, id, admin_id=admin_id, request_options=request_options + ) + return _response.data + + def list_tags(self, *, request_options: typing.Optional[RequestOptions] = None) -> TagList: + """ + You can fetch a list of all tags for a given workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TagList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.tags.list_tags() + """ + _response = self._raw_client.list_tags(request_options=request_options) + return _response.data + + def create_tag( + self, *, request: CreateTagRequestBody, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can use this endpoint to perform the following operations: + + **1. Create a new tag:** You can create a new tag by passing in the tag name as specified in "Create or Update Tag Request Payload" described below. + + **2. Update an existing tag:** You can update an existing tag by passing the id of the tag as specified in "Create or Update Tag Request Payload" described below. + + **3. Tag Companies:** You can tag single company or a list of companies. You can tag a company by passing in the tag name and the company details as specified in "Tag Company Request Payload" described below. Also, if the tag doesn't exist then a new one will be created automatically. + + **4. Untag Companies:** You can untag a single company or a list of companies. You can untag a company by passing in the tag id and the company details as specified in "Untag Company Request Payload" described below. + + **5. Tag Multiple Users:** You can tag a list of users. You can tag the users by passing in the tag name and the user details as specified in "Tag Users Request Payload" described below. + + Each operation will return a tag object. + + Parameters + ---------- + request : CreateTagRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + Action successful + + Examples + -------- + from intercom import Intercom + from intercom.unstable import CreateOrUpdateTagRequest + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.tags.create_tag( + request=CreateOrUpdateTagRequest( + name="test", + ), + ) + """ + _response = self._raw_client.create_tag(request=request, request_options=request_options) + return _response.data + + def find_tag(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Tag: + """ + You can fetch the details of tags that are on the workspace by their id. + This will return a tag object. + + Parameters + ---------- + id : str + The unique identifier of a given tag + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + Tag found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.tags.find_tag( + id="123", + ) + """ + _response = self._raw_client.find_tag(id, request_options=request_options) + return _response.data + + def delete_tag(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + You can delete the details of tags that are on the workspace by passing in the id. + + Parameters + ---------- + id : str + The unique identifier of a given tag + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.tags.delete_tag( + id="123", + ) + """ + _response = self._raw_client.delete_tag(id, request_options=request_options) + return _response.data + + def attach_tag_to_ticket( + self, ticket_id: str, *, id: str, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can tag a specific ticket. This will return a tag object for the tag that was added to the ticket. + + Parameters + ---------- + ticket_id : str + ticket_id + + id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.tags.attach_tag_to_ticket( + ticket_id="64619700005694", + id="7522907", + admin_id="780", + ) + """ + _response = self._raw_client.attach_tag_to_ticket( + ticket_id, id=id, admin_id=admin_id, request_options=request_options + ) + return _response.data + + def detach_tag_from_ticket( + self, ticket_id: str, id: str, *, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can remove tag from a specific ticket. This will return a tag object for the tag that was removed from the ticket. + + Parameters + ---------- + ticket_id : str + ticket_id + + id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.tags.detach_tag_from_ticket( + ticket_id="64619700005694", + id="7522907", + admin_id="123", + ) + """ + _response = self._raw_client.detach_tag_from_ticket( + ticket_id, id, admin_id=admin_id, request_options=request_options + ) + return _response.data + + +class AsyncTagsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawTagsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawTagsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawTagsClient + """ + return self._raw_client + + async def attach_tag_to_contact( + self, contact_id: str, *, id: str, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can tag a specific contact. This will return a tag object for the tag that was added to the contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + id : str + The unique identifier for the tag which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.tags.attach_tag_to_contact( + contact_id="63a07ddf05a32042dffac965", + id="7522907", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.attach_tag_to_contact(contact_id, id=id, request_options=request_options) + return _response.data + + async def detach_tag_from_contact( + self, contact_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can remove tag from a specific contact. This will return a tag object for the tag that was removed from the contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + id : str + The unique identifier for the tag which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.tags.detach_tag_from_contact( + contact_id="63a07ddf05a32042dffac965", + id="7522907", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.detach_tag_from_contact(contact_id, id, request_options=request_options) + return _response.data + + async def attach_tag_to_conversation( + self, conversation_id: str, *, id: str, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can tag a specific conversation. This will return a tag object for the tag that was added to the conversation. + + Parameters + ---------- + conversation_id : str + conversation_id + + id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.tags.attach_tag_to_conversation( + conversation_id="64619700005694", + id="7522907", + admin_id="780", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.attach_tag_to_conversation( + conversation_id, id=id, admin_id=admin_id, request_options=request_options + ) + return _response.data + + async def detach_tag_from_conversation( + self, conversation_id: str, id: str, *, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can remove tag from a specific conversation. This will return a tag object for the tag that was removed from the conversation. + + Parameters + ---------- + conversation_id : str + conversation_id + + id : str + id + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.tags.detach_tag_from_conversation( + conversation_id="64619700005694", + id="7522907", + admin_id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.detach_tag_from_conversation( + conversation_id, id, admin_id=admin_id, request_options=request_options + ) + return _response.data + + async def list_tags(self, *, request_options: typing.Optional[RequestOptions] = None) -> TagList: + """ + You can fetch a list of all tags for a given workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TagList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.tags.list_tags() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_tags(request_options=request_options) + return _response.data + + async def create_tag( + self, *, request: CreateTagRequestBody, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can use this endpoint to perform the following operations: + + **1. Create a new tag:** You can create a new tag by passing in the tag name as specified in "Create or Update Tag Request Payload" described below. + + **2. Update an existing tag:** You can update an existing tag by passing the id of the tag as specified in "Create or Update Tag Request Payload" described below. + + **3. Tag Companies:** You can tag single company or a list of companies. You can tag a company by passing in the tag name and the company details as specified in "Tag Company Request Payload" described below. Also, if the tag doesn't exist then a new one will be created automatically. + + **4. Untag Companies:** You can untag a single company or a list of companies. You can untag a company by passing in the tag id and the company details as specified in "Untag Company Request Payload" described below. + + **5. Tag Multiple Users:** You can tag a list of users. You can tag the users by passing in the tag name and the user details as specified in "Tag Users Request Payload" described below. + + Each operation will return a tag object. + + Parameters + ---------- + request : CreateTagRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + Action successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.unstable import CreateOrUpdateTagRequest + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.tags.create_tag( + request=CreateOrUpdateTagRequest( + name="test", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_tag(request=request, request_options=request_options) + return _response.data + + async def find_tag(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Tag: + """ + You can fetch the details of tags that are on the workspace by their id. + This will return a tag object. + + Parameters + ---------- + id : str + The unique identifier of a given tag + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + Tag found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.tags.find_tag( + id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.find_tag(id, request_options=request_options) + return _response.data + + async def delete_tag(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + You can delete the details of tags that are on the workspace by passing in the id. + + Parameters + ---------- + id : str + The unique identifier of a given tag + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.tags.delete_tag( + id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_tag(id, request_options=request_options) + return _response.data + + async def attach_tag_to_ticket( + self, ticket_id: str, *, id: str, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can tag a specific ticket. This will return a tag object for the tag that was added to the ticket. + + Parameters + ---------- + ticket_id : str + ticket_id + + id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.tags.attach_tag_to_ticket( + ticket_id="64619700005694", + id="7522907", + admin_id="780", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.attach_tag_to_ticket( + ticket_id, id=id, admin_id=admin_id, request_options=request_options + ) + return _response.data + + async def detach_tag_from_ticket( + self, ticket_id: str, id: str, *, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> Tag: + """ + You can remove tag from a specific ticket. This will return a tag object for the tag that was removed from the ticket. + + Parameters + ---------- + ticket_id : str + ticket_id + + id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Tag + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.tags.detach_tag_from_ticket( + ticket_id="64619700005694", + id="7522907", + admin_id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.detach_tag_from_ticket( + ticket_id, id, admin_id=admin_id, request_options=request_options + ) + return _response.data diff --git a/src/intercom/unstable/tags/raw_client.py b/src/intercom/unstable/tags/raw_client.py new file mode 100644 index 00000000..d9ea351d --- /dev/null +++ b/src/intercom/unstable/tags/raw_client.py @@ -0,0 +1,1416 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.serialization import convert_and_respect_annotation_metadata +from ...core.unchecked_base_model import construct_type +from ..errors.bad_request_error import BadRequestError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from ..types.tag_list import TagList +from .types.create_tag_request_body import CreateTagRequestBody +from .types.tag import Tag + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawTagsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def attach_tag_to_contact( + self, contact_id: str, *, id: str, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Tag]: + """ + You can tag a specific contact. This will return a tag object for the tag that was added to the contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + id : str + The unique identifier for the tag which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Tag] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(contact_id)}/tags", + method="POST", + json={ + "id": id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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_tag_from_contact( + self, contact_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Tag]: + """ + You can remove tag from a specific contact. This will return a tag object for the tag that was removed from the contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + id : str + The unique identifier for the tag which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Tag] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(contact_id)}/tags/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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_tag_to_conversation( + self, conversation_id: str, *, id: str, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Tag]: + """ + You can tag a specific conversation. This will return a tag object for the tag that was added to the conversation. + + Parameters + ---------- + conversation_id : str + conversation_id + + id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Tag] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/tags", + method="POST", + json={ + "id": id, + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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_tag_from_conversation( + self, conversation_id: str, id: str, *, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Tag]: + """ + You can remove tag from a specific conversation. This will return a tag object for the tag that was removed from the conversation. + + Parameters + ---------- + conversation_id : str + conversation_id + + id : str + id + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Tag] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/tags/{jsonable_encoder(id)}", + method="DELETE", + json={ + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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_tags(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[TagList]: + """ + You can fetch a list of all tags for a given workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[TagList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "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(), + ), + ), + ) + _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_tag( + self, *, request: CreateTagRequestBody, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Tag]: + """ + You can use this endpoint to perform the following operations: + + **1. Create a new tag:** You can create a new tag by passing in the tag name as specified in "Create or Update Tag Request Payload" described below. + + **2. Update an existing tag:** You can update an existing tag by passing the id of the tag as specified in "Create or Update Tag Request Payload" described below. + + **3. Tag Companies:** You can tag single company or a list of companies. You can tag a company by passing in the tag name and the company details as specified in "Tag Company Request Payload" described below. Also, if the tag doesn't exist then a new one will be created automatically. + + **4. Untag Companies:** You can untag a single company or a list of companies. You can untag a company by passing in the tag id and the company details as specified in "Untag Company Request Payload" described below. + + **5. Tag Multiple Users:** You can tag a list of users. You can tag the users by passing in the tag name and the user details as specified in "Tag Users Request Payload" described below. + + Each operation will return a tag object. + + Parameters + ---------- + request : CreateTagRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Tag] + Action successful + """ + _response = self._client_wrapper.httpx_client.request( + "tags", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateTagRequestBody, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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_tag(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Tag]: + """ + You can fetch the details of tags that are on the workspace by their id. + This will return a tag object. + + Parameters + ---------- + id : str + The unique identifier of a given tag + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Tag] + Tag found + """ + _response = self._client_wrapper.httpx_client.request( + f"tags/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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 delete_tag(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[None]: + """ + You can delete the details of tags that are on the workspace by passing in the id. + + Parameters + ---------- + id : str + The unique identifier of a given tag + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + f"tags/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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_tag_to_ticket( + self, ticket_id: str, *, id: str, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Tag]: + """ + You can tag a specific ticket. This will return a tag object for the tag that was added to the ticket. + + Parameters + ---------- + ticket_id : str + ticket_id + + id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Tag] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(ticket_id)}/tags", + method="POST", + json={ + "id": id, + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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_tag_from_ticket( + self, ticket_id: str, id: str, *, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[Tag]: + """ + You can remove tag from a specific ticket. This will return a tag object for the tag that was removed from the ticket. + + Parameters + ---------- + ticket_id : str + ticket_id + + id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Tag] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(ticket_id)}/tags/{jsonable_encoder(id)}", + method="DELETE", + json={ + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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) + + +class AsyncRawTagsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def attach_tag_to_contact( + self, contact_id: str, *, id: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Tag]: + """ + You can tag a specific contact. This will return a tag object for the tag that was added to the contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + id : str + The unique identifier for the tag which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Tag] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(contact_id)}/tags", + method="POST", + json={ + "id": id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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_tag_from_contact( + self, contact_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Tag]: + """ + You can remove tag from a specific contact. This will return a tag object for the tag that was removed from the contact. + + Parameters + ---------- + contact_id : str + The unique identifier for the contact which is given by Intercom + + id : str + The unique identifier for the tag which is given by Intercom + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Tag] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"contacts/{jsonable_encoder(contact_id)}/tags/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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_tag_to_conversation( + self, conversation_id: str, *, id: str, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Tag]: + """ + You can tag a specific conversation. This will return a tag object for the tag that was added to the conversation. + + Parameters + ---------- + conversation_id : str + conversation_id + + id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Tag] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/tags", + method="POST", + json={ + "id": id, + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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_tag_from_conversation( + self, conversation_id: str, id: str, *, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Tag]: + """ + You can remove tag from a specific conversation. This will return a tag object for the tag that was removed from the conversation. + + Parameters + ---------- + conversation_id : str + conversation_id + + id : str + id + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Tag] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/tags/{jsonable_encoder(id)}", + method="DELETE", + json={ + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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_tags(self, *, request_options: typing.Optional[RequestOptions] = None) -> AsyncHttpResponse[TagList]: + """ + You can fetch a list of all tags for a given workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[TagList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "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(), + ), + ), + ) + _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_tag( + self, *, request: CreateTagRequestBody, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Tag]: + """ + You can use this endpoint to perform the following operations: + + **1. Create a new tag:** You can create a new tag by passing in the tag name as specified in "Create or Update Tag Request Payload" described below. + + **2. Update an existing tag:** You can update an existing tag by passing the id of the tag as specified in "Create or Update Tag Request Payload" described below. + + **3. Tag Companies:** You can tag single company or a list of companies. You can tag a company by passing in the tag name and the company details as specified in "Tag Company Request Payload" described below. Also, if the tag doesn't exist then a new one will be created automatically. + + **4. Untag Companies:** You can untag a single company or a list of companies. You can untag a company by passing in the tag id and the company details as specified in "Untag Company Request Payload" described below. + + **5. Tag Multiple Users:** You can tag a list of users. You can tag the users by passing in the tag name and the user details as specified in "Tag Users Request Payload" described below. + + Each operation will return a tag object. + + Parameters + ---------- + request : CreateTagRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Tag] + Action successful + """ + _response = await self._client_wrapper.httpx_client.request( + "tags", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=CreateTagRequestBody, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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_tag( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Tag]: + """ + You can fetch the details of tags that are on the workspace by their id. + This will return a tag object. + + Parameters + ---------- + id : str + The unique identifier of a given tag + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Tag] + Tag found + """ + _response = await self._client_wrapper.httpx_client.request( + f"tags/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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 delete_tag( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + You can delete the details of tags that are on the workspace by passing in the id. + + Parameters + ---------- + id : str + The unique identifier of a given tag + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + f"tags/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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_tag_to_ticket( + self, ticket_id: str, *, id: str, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Tag]: + """ + You can tag a specific ticket. This will return a tag object for the tag that was added to the ticket. + + Parameters + ---------- + ticket_id : str + ticket_id + + id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Tag] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(ticket_id)}/tags", + method="POST", + json={ + "id": id, + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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_tag_from_ticket( + self, ticket_id: str, id: str, *, admin_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Tag]: + """ + You can remove tag from a specific ticket. This will return a tag object for the tag that was removed from the ticket. + + Parameters + ---------- + ticket_id : str + ticket_id + + id : str + The unique identifier for the tag which is given by Intercom + + admin_id : str + The unique identifier for the admin which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Tag] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(ticket_id)}/tags/{jsonable_encoder(id)}", + method="DELETE", + json={ + "admin_id": admin_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Tag, + construct_type( + type_=Tag, # 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) diff --git a/src/intercom/unstable/tags/types/__init__.py b/src/intercom/unstable/tags/types/__init__.py new file mode 100644 index 00000000..0f3ae405 --- /dev/null +++ b/src/intercom/unstable/tags/types/__init__.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .create_tag_request_body import CreateTagRequestBody + from .tag import Tag + from .tag_basic import TagBasic +_dynamic_imports: typing.Dict[str, str] = { + "CreateTagRequestBody": ".create_tag_request_body", + "Tag": ".tag", + "TagBasic": ".tag_basic", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["CreateTagRequestBody", "Tag", "TagBasic"] diff --git a/src/intercom/unstable/tags/types/create_tag_request_body.py b/src/intercom/unstable/tags/types/create_tag_request_body.py new file mode 100644 index 00000000..bb1dd87e --- /dev/null +++ b/src/intercom/unstable/tags/types/create_tag_request_body.py @@ -0,0 +1,12 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...types.create_or_update_tag_request import CreateOrUpdateTagRequest +from ...types.tag_company_request import TagCompanyRequest +from ...types.tag_multiple_users_request import TagMultipleUsersRequest +from ...types.untag_company_request import UntagCompanyRequest + +CreateTagRequestBody = typing.Union[ + CreateOrUpdateTagRequest, TagCompanyRequest, UntagCompanyRequest, TagMultipleUsersRequest +] diff --git a/src/intercom/unstable/tags/types/tag.py b/src/intercom/unstable/tags/types/tag.py new file mode 100644 index 00000000..160fbbfc --- /dev/null +++ b/src/intercom/unstable/tags/types/tag.py @@ -0,0 +1,45 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...types.reference import Reference + + +class Tag(UncheckedBaseModel): + """ + A tag allows you to label your contacts, companies, and conversations and list them using that tag. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + value is "tag" + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the tag + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the tag + """ + + applied_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the tag was applied to the object + """ + + applied_by: typing.Optional[Reference] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/tags/types/tag_basic.py b/src/intercom/unstable/tags/types/tag_basic.py new file mode 100644 index 00000000..b95a9c8e --- /dev/null +++ b/src/intercom/unstable/tags/types/tag_basic.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class TagBasic(UncheckedBaseModel): + """ + A tag allows you to label your contacts, companies, and conversations and list them using that tag. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + value is "tag" + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the tag + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the tag + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/teams/__init__.py b/src/intercom/unstable/teams/__init__.py new file mode 100644 index 00000000..0d71c0eb --- /dev/null +++ b/src/intercom/unstable/teams/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import Team +_dynamic_imports: typing.Dict[str, str] = {"Team": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Team"] diff --git a/src/intercom/unstable/teams/client.py b/src/intercom/unstable/teams/client.py new file mode 100644 index 00000000..7766c94c --- /dev/null +++ b/src/intercom/unstable/teams/client.py @@ -0,0 +1,171 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..types.team_list import TeamList +from .raw_client import AsyncRawTeamsClient, RawTeamsClient +from .types.team import Team + + +class TeamsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawTeamsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawTeamsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawTeamsClient + """ + return self._raw_client + + def list_teams(self, *, request_options: typing.Optional[RequestOptions] = None) -> TeamList: + """ + This will return a list of team objects for the App. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TeamList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.teams.list_teams() + """ + _response = self._raw_client.list_teams(request_options=request_options) + return _response.data + + def retrieve_team(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Team: + """ + You can fetch the details of a single team, containing an array of admins that belong to this team. + + Parameters + ---------- + id : str + The unique identifier of a given team. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Team + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.teams.retrieve_team( + id="123", + ) + """ + _response = self._raw_client.retrieve_team(id, request_options=request_options) + return _response.data + + +class AsyncTeamsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawTeamsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawTeamsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawTeamsClient + """ + return self._raw_client + + async def list_teams(self, *, request_options: typing.Optional[RequestOptions] = None) -> TeamList: + """ + This will return a list of team objects for the App. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TeamList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.teams.list_teams() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_teams(request_options=request_options) + return _response.data + + async def retrieve_team(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> Team: + """ + You can fetch the details of a single team, containing an array of admins that belong to this team. + + Parameters + ---------- + id : str + The unique identifier of a given team. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Team + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.teams.retrieve_team( + id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.retrieve_team(id, request_options=request_options) + return _response.data diff --git a/src/intercom/unstable/teams/raw_client.py b/src/intercom/unstable/teams/raw_client.py new file mode 100644 index 00000000..111a98c1 --- /dev/null +++ b/src/intercom/unstable/teams/raw_client.py @@ -0,0 +1,238 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from ..types.team_list import TeamList +from .types.team import Team + + +class RawTeamsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_teams(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[TeamList]: + """ + This will return a list of team objects for the App. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[TeamList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "teams", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TeamList, + construct_type( + type_=TeamList, # 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 retrieve_team(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[Team]: + """ + You can fetch the details of a single team, containing an array of admins that belong to this team. + + Parameters + ---------- + id : str + The unique identifier of a given team. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Team] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"teams/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Team, + construct_type( + type_=Team, # 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) + + +class AsyncRawTeamsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_teams( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[TeamList]: + """ + This will return a list of team objects for the App. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[TeamList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "teams", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TeamList, + construct_type( + type_=TeamList, # 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 retrieve_team( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[Team]: + """ + You can fetch the details of a single team, containing an array of admins that belong to this team. + + Parameters + ---------- + id : str + The unique identifier of a given team. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Team] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"teams/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Team, + construct_type( + type_=Team, # 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) diff --git a/src/intercom/unstable/teams/types/__init__.py b/src/intercom/unstable/teams/types/__init__.py new file mode 100644 index 00000000..b3e03912 --- /dev/null +++ b/src/intercom/unstable/teams/types/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .team import Team +_dynamic_imports: typing.Dict[str, str] = {"Team": ".team"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["Team"] diff --git a/src/intercom/unstable/teams/types/team.py b/src/intercom/unstable/teams/types/team.py new file mode 100644 index 00000000..6ce3c60d --- /dev/null +++ b/src/intercom/unstable/teams/types/team.py @@ -0,0 +1,45 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...types.admin_priority_level import AdminPriorityLevel + + +class Team(UncheckedBaseModel): + """ + Teams are groups of admins in Intercom. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + Value is always "team" + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the team + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the team + """ + + admin_ids: typing.Optional[typing.List[int]] = pydantic.Field(default=None) + """ + The list of admin IDs that are a part of the team. + """ + + admin_priority_level: typing.Optional[AdminPriorityLevel] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/ticket_states/__init__.py b/src/intercom/unstable/ticket_states/__init__.py new file mode 100644 index 00000000..5cde0202 --- /dev/null +++ b/src/intercom/unstable/ticket_states/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/src/intercom/unstable/ticket_states/client.py b/src/intercom/unstable/ticket_states/client.py new file mode 100644 index 00000000..3e08bd05 --- /dev/null +++ b/src/intercom/unstable/ticket_states/client.py @@ -0,0 +1,100 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..types.ticket_state_list import TicketStateList +from .raw_client import AsyncRawTicketStatesClient, RawTicketStatesClient + + +class TicketStatesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawTicketStatesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawTicketStatesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawTicketStatesClient + """ + return self._raw_client + + def list_ticket_states(self, *, request_options: typing.Optional[RequestOptions] = None) -> TicketStateList: + """ + You can get a list of all ticket states for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TicketStateList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.ticket_states.list_ticket_states() + """ + _response = self._raw_client.list_ticket_states(request_options=request_options) + return _response.data + + +class AsyncTicketStatesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawTicketStatesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawTicketStatesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawTicketStatesClient + """ + return self._raw_client + + async def list_ticket_states(self, *, request_options: typing.Optional[RequestOptions] = None) -> TicketStateList: + """ + You can get a list of all ticket states for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TicketStateList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.ticket_states.list_ticket_states() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_ticket_states(request_options=request_options) + return _response.data diff --git a/src/intercom/unstable/ticket_states/raw_client.py b/src/intercom/unstable/ticket_states/raw_client.py new file mode 100644 index 00000000..b17ac0ac --- /dev/null +++ b/src/intercom/unstable/ticket_states/raw_client.py @@ -0,0 +1,117 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.http_response import AsyncHttpResponse, HttpResponse +from ...core.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from ..types.ticket_state_list import TicketStateList + + +class RawTicketStatesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_ticket_states( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[TicketStateList]: + """ + You can get a list of all ticket states for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[TicketStateList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "ticket_states", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TicketStateList, + construct_type( + type_=TicketStateList, # 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) + + +class AsyncRawTicketStatesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_ticket_states( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[TicketStateList]: + """ + You can get a list of all ticket states for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[TicketStateList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "ticket_states", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TicketStateList, + construct_type( + type_=TicketStateList, # 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) diff --git a/src/intercom/unstable/ticket_type_attributes/__init__.py b/src/intercom/unstable/ticket_type_attributes/__init__.py new file mode 100644 index 00000000..12db5aa8 --- /dev/null +++ b/src/intercom/unstable/ticket_type_attributes/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import CreateTicketTypeAttributeRequestDataType +_dynamic_imports: typing.Dict[str, str] = {"CreateTicketTypeAttributeRequestDataType": ".types"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["CreateTicketTypeAttributeRequestDataType"] diff --git a/src/intercom/unstable/ticket_type_attributes/client.py b/src/intercom/unstable/ticket_type_attributes/client.py new file mode 100644 index 00000000..955f38ff --- /dev/null +++ b/src/intercom/unstable/ticket_type_attributes/client.py @@ -0,0 +1,438 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..types.ticket_type_attribute import TicketTypeAttribute +from .raw_client import AsyncRawTicketTypeAttributesClient, RawTicketTypeAttributesClient +from .types.create_ticket_type_attribute_request_data_type import CreateTicketTypeAttributeRequestDataType + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class TicketTypeAttributesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawTicketTypeAttributesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawTicketTypeAttributesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawTicketTypeAttributesClient + """ + return self._raw_client + + def create_ticket_type_attribute( + self, + ticket_type_id: str, + *, + name: str, + description: str, + data_type: CreateTicketTypeAttributeRequestDataType, + required_to_create: typing.Optional[bool] = OMIT, + required_to_create_for_contacts: typing.Optional[bool] = OMIT, + visible_on_create: typing.Optional[bool] = OMIT, + visible_to_contacts: typing.Optional[bool] = OMIT, + multiline: typing.Optional[bool] = OMIT, + list_items: typing.Optional[str] = OMIT, + allow_multiple_values: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[TicketTypeAttribute]: + """ + You can create a new attribute for a ticket type. + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + name : str + The name of the ticket type attribute + + description : str + The description of the attribute presented to the teammate or contact + + data_type : CreateTicketTypeAttributeRequestDataType + The data type of the attribute + + required_to_create : typing.Optional[bool] + Whether the attribute is required to be filled in when teammates are creating the ticket in Inbox. + + required_to_create_for_contacts : typing.Optional[bool] + Whether the attribute is required to be filled in when contacts are creating the ticket in Messenger. + + visible_on_create : typing.Optional[bool] + Whether the attribute is visible to teammates when creating a ticket in Inbox. + + visible_to_contacts : typing.Optional[bool] + Whether the attribute is visible to contacts when creating a ticket in Messenger. + + multiline : typing.Optional[bool] + Whether the attribute allows multiple lines of text (only applicable to string attributes) + + list_items : typing.Optional[str] + A comma delimited list of items for the attribute value (only applicable to list attributes) + + allow_multiple_values : typing.Optional[bool] + Whether the attribute allows multiple files to be attached to it (only applicable to file attributes) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[TicketTypeAttribute] + Ticket Type Attribute created + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.ticket_type_attributes.create_ticket_type_attribute( + ticket_type_id="ticket_type_id", + name="Attribute Title", + description="Attribute Description", + data_type="string", + required_to_create=False, + ) + """ + _response = self._raw_client.create_ticket_type_attribute( + ticket_type_id, + name=name, + description=description, + data_type=data_type, + required_to_create=required_to_create, + required_to_create_for_contacts=required_to_create_for_contacts, + visible_on_create=visible_on_create, + visible_to_contacts=visible_to_contacts, + multiline=multiline, + list_items=list_items, + allow_multiple_values=allow_multiple_values, + request_options=request_options, + ) + return _response.data + + def update_ticket_type_attribute( + self, + ticket_type_id: str, + id: str, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + required_to_create: typing.Optional[bool] = OMIT, + required_to_create_for_contacts: typing.Optional[bool] = OMIT, + visible_on_create: typing.Optional[bool] = OMIT, + visible_to_contacts: typing.Optional[bool] = OMIT, + multiline: typing.Optional[bool] = OMIT, + list_items: typing.Optional[str] = OMIT, + allow_multiple_values: typing.Optional[bool] = OMIT, + archived: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[TicketTypeAttribute]: + """ + You can update an existing attribute for a ticket type. + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + id : str + The unique identifier for the ticket type attribute which is given by Intercom. + + name : typing.Optional[str] + The name of the ticket type attribute + + description : typing.Optional[str] + The description of the attribute presented to the teammate or contact + + required_to_create : typing.Optional[bool] + Whether the attribute is required to be filled in when teammates are creating the ticket in Inbox. + + required_to_create_for_contacts : typing.Optional[bool] + Whether the attribute is required to be filled in when contacts are creating the ticket in Messenger. + + visible_on_create : typing.Optional[bool] + Whether the attribute is visible to teammates when creating a ticket in Inbox. + + visible_to_contacts : typing.Optional[bool] + Whether the attribute is visible to contacts when creating a ticket in Messenger. + + multiline : typing.Optional[bool] + Whether the attribute allows multiple lines of text (only applicable to string attributes) + + list_items : typing.Optional[str] + A comma delimited list of items for the attribute value (only applicable to list attributes) + + allow_multiple_values : typing.Optional[bool] + Whether the attribute allows multiple files to be attached to it (only applicable to file attributes) + + archived : typing.Optional[bool] + Whether the attribute should be archived and not shown during creation of the ticket (it will still be present on previously created tickets) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[TicketTypeAttribute] + Ticket Type Attribute updated + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.ticket_type_attributes.update_ticket_type_attribute( + ticket_type_id="ticket_type_id", + id="id", + description="New Attribute Description", + ) + """ + _response = self._raw_client.update_ticket_type_attribute( + ticket_type_id, + id, + name=name, + description=description, + required_to_create=required_to_create, + required_to_create_for_contacts=required_to_create_for_contacts, + visible_on_create=visible_on_create, + visible_to_contacts=visible_to_contacts, + multiline=multiline, + list_items=list_items, + allow_multiple_values=allow_multiple_values, + archived=archived, + request_options=request_options, + ) + return _response.data + + +class AsyncTicketTypeAttributesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawTicketTypeAttributesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawTicketTypeAttributesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawTicketTypeAttributesClient + """ + return self._raw_client + + async def create_ticket_type_attribute( + self, + ticket_type_id: str, + *, + name: str, + description: str, + data_type: CreateTicketTypeAttributeRequestDataType, + required_to_create: typing.Optional[bool] = OMIT, + required_to_create_for_contacts: typing.Optional[bool] = OMIT, + visible_on_create: typing.Optional[bool] = OMIT, + visible_to_contacts: typing.Optional[bool] = OMIT, + multiline: typing.Optional[bool] = OMIT, + list_items: typing.Optional[str] = OMIT, + allow_multiple_values: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[TicketTypeAttribute]: + """ + You can create a new attribute for a ticket type. + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + name : str + The name of the ticket type attribute + + description : str + The description of the attribute presented to the teammate or contact + + data_type : CreateTicketTypeAttributeRequestDataType + The data type of the attribute + + required_to_create : typing.Optional[bool] + Whether the attribute is required to be filled in when teammates are creating the ticket in Inbox. + + required_to_create_for_contacts : typing.Optional[bool] + Whether the attribute is required to be filled in when contacts are creating the ticket in Messenger. + + visible_on_create : typing.Optional[bool] + Whether the attribute is visible to teammates when creating a ticket in Inbox. + + visible_to_contacts : typing.Optional[bool] + Whether the attribute is visible to contacts when creating a ticket in Messenger. + + multiline : typing.Optional[bool] + Whether the attribute allows multiple lines of text (only applicable to string attributes) + + list_items : typing.Optional[str] + A comma delimited list of items for the attribute value (only applicable to list attributes) + + allow_multiple_values : typing.Optional[bool] + Whether the attribute allows multiple files to be attached to it (only applicable to file attributes) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[TicketTypeAttribute] + Ticket Type Attribute created + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.ticket_type_attributes.create_ticket_type_attribute( + ticket_type_id="ticket_type_id", + name="Attribute Title", + description="Attribute Description", + data_type="string", + required_to_create=False, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_ticket_type_attribute( + ticket_type_id, + name=name, + description=description, + data_type=data_type, + required_to_create=required_to_create, + required_to_create_for_contacts=required_to_create_for_contacts, + visible_on_create=visible_on_create, + visible_to_contacts=visible_to_contacts, + multiline=multiline, + list_items=list_items, + allow_multiple_values=allow_multiple_values, + request_options=request_options, + ) + return _response.data + + async def update_ticket_type_attribute( + self, + ticket_type_id: str, + id: str, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + required_to_create: typing.Optional[bool] = OMIT, + required_to_create_for_contacts: typing.Optional[bool] = OMIT, + visible_on_create: typing.Optional[bool] = OMIT, + visible_to_contacts: typing.Optional[bool] = OMIT, + multiline: typing.Optional[bool] = OMIT, + list_items: typing.Optional[str] = OMIT, + allow_multiple_values: typing.Optional[bool] = OMIT, + archived: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[TicketTypeAttribute]: + """ + You can update an existing attribute for a ticket type. + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + id : str + The unique identifier for the ticket type attribute which is given by Intercom. + + name : typing.Optional[str] + The name of the ticket type attribute + + description : typing.Optional[str] + The description of the attribute presented to the teammate or contact + + required_to_create : typing.Optional[bool] + Whether the attribute is required to be filled in when teammates are creating the ticket in Inbox. + + required_to_create_for_contacts : typing.Optional[bool] + Whether the attribute is required to be filled in when contacts are creating the ticket in Messenger. + + visible_on_create : typing.Optional[bool] + Whether the attribute is visible to teammates when creating a ticket in Inbox. + + visible_to_contacts : typing.Optional[bool] + Whether the attribute is visible to contacts when creating a ticket in Messenger. + + multiline : typing.Optional[bool] + Whether the attribute allows multiple lines of text (only applicable to string attributes) + + list_items : typing.Optional[str] + A comma delimited list of items for the attribute value (only applicable to list attributes) + + allow_multiple_values : typing.Optional[bool] + Whether the attribute allows multiple files to be attached to it (only applicable to file attributes) + + archived : typing.Optional[bool] + Whether the attribute should be archived and not shown during creation of the ticket (it will still be present on previously created tickets) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[TicketTypeAttribute] + Ticket Type Attribute updated + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.ticket_type_attributes.update_ticket_type_attribute( + ticket_type_id="ticket_type_id", + id="id", + description="New Attribute Description", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update_ticket_type_attribute( + ticket_type_id, + id, + name=name, + description=description, + required_to_create=required_to_create, + required_to_create_for_contacts=required_to_create_for_contacts, + visible_on_create=visible_on_create, + visible_to_contacts=visible_to_contacts, + multiline=multiline, + list_items=list_items, + allow_multiple_values=allow_multiple_values, + archived=archived, + request_options=request_options, + ) + return _response.data diff --git a/src/intercom/unstable/ticket_type_attributes/raw_client.py b/src/intercom/unstable/ticket_type_attributes/raw_client.py new file mode 100644 index 00000000..c05d357b --- /dev/null +++ b/src/intercom/unstable/ticket_type_attributes/raw_client.py @@ -0,0 +1,480 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from ..types.ticket_type_attribute import TicketTypeAttribute +from .types.create_ticket_type_attribute_request_data_type import CreateTicketTypeAttributeRequestDataType + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawTicketTypeAttributesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def create_ticket_type_attribute( + self, + ticket_type_id: str, + *, + name: str, + description: str, + data_type: CreateTicketTypeAttributeRequestDataType, + required_to_create: typing.Optional[bool] = OMIT, + required_to_create_for_contacts: typing.Optional[bool] = OMIT, + visible_on_create: typing.Optional[bool] = OMIT, + visible_to_contacts: typing.Optional[bool] = OMIT, + multiline: typing.Optional[bool] = OMIT, + list_items: typing.Optional[str] = OMIT, + allow_multiple_values: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[typing.Optional[TicketTypeAttribute]]: + """ + You can create a new attribute for a ticket type. + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + name : str + The name of the ticket type attribute + + description : str + The description of the attribute presented to the teammate or contact + + data_type : CreateTicketTypeAttributeRequestDataType + The data type of the attribute + + required_to_create : typing.Optional[bool] + Whether the attribute is required to be filled in when teammates are creating the ticket in Inbox. + + required_to_create_for_contacts : typing.Optional[bool] + Whether the attribute is required to be filled in when contacts are creating the ticket in Messenger. + + visible_on_create : typing.Optional[bool] + Whether the attribute is visible to teammates when creating a ticket in Inbox. + + visible_to_contacts : typing.Optional[bool] + Whether the attribute is visible to contacts when creating a ticket in Messenger. + + multiline : typing.Optional[bool] + Whether the attribute allows multiple lines of text (only applicable to string attributes) + + list_items : typing.Optional[str] + A comma delimited list of items for the attribute value (only applicable to list attributes) + + allow_multiple_values : typing.Optional[bool] + Whether the attribute allows multiple files to be attached to it (only applicable to file attributes) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[TicketTypeAttribute]] + Ticket Type Attribute created + """ + _response = self._client_wrapper.httpx_client.request( + f"ticket_types/{jsonable_encoder(ticket_type_id)}/attributes", + method="POST", + json={ + "name": name, + "description": description, + "data_type": data_type, + "required_to_create": required_to_create, + "required_to_create_for_contacts": required_to_create_for_contacts, + "visible_on_create": visible_on_create, + "visible_to_contacts": visible_to_contacts, + "multiline": multiline, + "list_items": list_items, + "allow_multiple_values": allow_multiple_values, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[TicketTypeAttribute], + construct_type( + type_=typing.Optional[TicketTypeAttribute], # 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_ticket_type_attribute( + self, + ticket_type_id: str, + id: str, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + required_to_create: typing.Optional[bool] = OMIT, + required_to_create_for_contacts: typing.Optional[bool] = OMIT, + visible_on_create: typing.Optional[bool] = OMIT, + visible_to_contacts: typing.Optional[bool] = OMIT, + multiline: typing.Optional[bool] = OMIT, + list_items: typing.Optional[str] = OMIT, + allow_multiple_values: typing.Optional[bool] = OMIT, + archived: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[typing.Optional[TicketTypeAttribute]]: + """ + You can update an existing attribute for a ticket type. + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + id : str + The unique identifier for the ticket type attribute which is given by Intercom. + + name : typing.Optional[str] + The name of the ticket type attribute + + description : typing.Optional[str] + The description of the attribute presented to the teammate or contact + + required_to_create : typing.Optional[bool] + Whether the attribute is required to be filled in when teammates are creating the ticket in Inbox. + + required_to_create_for_contacts : typing.Optional[bool] + Whether the attribute is required to be filled in when contacts are creating the ticket in Messenger. + + visible_on_create : typing.Optional[bool] + Whether the attribute is visible to teammates when creating a ticket in Inbox. + + visible_to_contacts : typing.Optional[bool] + Whether the attribute is visible to contacts when creating a ticket in Messenger. + + multiline : typing.Optional[bool] + Whether the attribute allows multiple lines of text (only applicable to string attributes) + + list_items : typing.Optional[str] + A comma delimited list of items for the attribute value (only applicable to list attributes) + + allow_multiple_values : typing.Optional[bool] + Whether the attribute allows multiple files to be attached to it (only applicable to file attributes) + + archived : typing.Optional[bool] + Whether the attribute should be archived and not shown during creation of the ticket (it will still be present on previously created tickets) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[TicketTypeAttribute]] + Ticket Type Attribute updated + """ + _response = self._client_wrapper.httpx_client.request( + f"ticket_types/{jsonable_encoder(ticket_type_id)}/attributes/{jsonable_encoder(id)}", + method="PUT", + json={ + "name": name, + "description": description, + "required_to_create": required_to_create, + "required_to_create_for_contacts": required_to_create_for_contacts, + "visible_on_create": visible_on_create, + "visible_to_contacts": visible_to_contacts, + "multiline": multiline, + "list_items": list_items, + "allow_multiple_values": allow_multiple_values, + "archived": archived, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[TicketTypeAttribute], + construct_type( + type_=typing.Optional[TicketTypeAttribute], # 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) + + +class AsyncRawTicketTypeAttributesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def create_ticket_type_attribute( + self, + ticket_type_id: str, + *, + name: str, + description: str, + data_type: CreateTicketTypeAttributeRequestDataType, + required_to_create: typing.Optional[bool] = OMIT, + required_to_create_for_contacts: typing.Optional[bool] = OMIT, + visible_on_create: typing.Optional[bool] = OMIT, + visible_to_contacts: typing.Optional[bool] = OMIT, + multiline: typing.Optional[bool] = OMIT, + list_items: typing.Optional[str] = OMIT, + allow_multiple_values: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[typing.Optional[TicketTypeAttribute]]: + """ + You can create a new attribute for a ticket type. + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + name : str + The name of the ticket type attribute + + description : str + The description of the attribute presented to the teammate or contact + + data_type : CreateTicketTypeAttributeRequestDataType + The data type of the attribute + + required_to_create : typing.Optional[bool] + Whether the attribute is required to be filled in when teammates are creating the ticket in Inbox. + + required_to_create_for_contacts : typing.Optional[bool] + Whether the attribute is required to be filled in when contacts are creating the ticket in Messenger. + + visible_on_create : typing.Optional[bool] + Whether the attribute is visible to teammates when creating a ticket in Inbox. + + visible_to_contacts : typing.Optional[bool] + Whether the attribute is visible to contacts when creating a ticket in Messenger. + + multiline : typing.Optional[bool] + Whether the attribute allows multiple lines of text (only applicable to string attributes) + + list_items : typing.Optional[str] + A comma delimited list of items for the attribute value (only applicable to list attributes) + + allow_multiple_values : typing.Optional[bool] + Whether the attribute allows multiple files to be attached to it (only applicable to file attributes) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[TicketTypeAttribute]] + Ticket Type Attribute created + """ + _response = await self._client_wrapper.httpx_client.request( + f"ticket_types/{jsonable_encoder(ticket_type_id)}/attributes", + method="POST", + json={ + "name": name, + "description": description, + "data_type": data_type, + "required_to_create": required_to_create, + "required_to_create_for_contacts": required_to_create_for_contacts, + "visible_on_create": visible_on_create, + "visible_to_contacts": visible_to_contacts, + "multiline": multiline, + "list_items": list_items, + "allow_multiple_values": allow_multiple_values, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[TicketTypeAttribute], + construct_type( + type_=typing.Optional[TicketTypeAttribute], # 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_ticket_type_attribute( + self, + ticket_type_id: str, + id: str, + *, + name: typing.Optional[str] = OMIT, + description: typing.Optional[str] = OMIT, + required_to_create: typing.Optional[bool] = OMIT, + required_to_create_for_contacts: typing.Optional[bool] = OMIT, + visible_on_create: typing.Optional[bool] = OMIT, + visible_to_contacts: typing.Optional[bool] = OMIT, + multiline: typing.Optional[bool] = OMIT, + list_items: typing.Optional[str] = OMIT, + allow_multiple_values: typing.Optional[bool] = OMIT, + archived: typing.Optional[bool] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[typing.Optional[TicketTypeAttribute]]: + """ + You can update an existing attribute for a ticket type. + + Parameters + ---------- + ticket_type_id : str + The unique identifier for the ticket type which is given by Intercom. + + id : str + The unique identifier for the ticket type attribute which is given by Intercom. + + name : typing.Optional[str] + The name of the ticket type attribute + + description : typing.Optional[str] + The description of the attribute presented to the teammate or contact + + required_to_create : typing.Optional[bool] + Whether the attribute is required to be filled in when teammates are creating the ticket in Inbox. + + required_to_create_for_contacts : typing.Optional[bool] + Whether the attribute is required to be filled in when contacts are creating the ticket in Messenger. + + visible_on_create : typing.Optional[bool] + Whether the attribute is visible to teammates when creating a ticket in Inbox. + + visible_to_contacts : typing.Optional[bool] + Whether the attribute is visible to contacts when creating a ticket in Messenger. + + multiline : typing.Optional[bool] + Whether the attribute allows multiple lines of text (only applicable to string attributes) + + list_items : typing.Optional[str] + A comma delimited list of items for the attribute value (only applicable to list attributes) + + allow_multiple_values : typing.Optional[bool] + Whether the attribute allows multiple files to be attached to it (only applicable to file attributes) + + archived : typing.Optional[bool] + Whether the attribute should be archived and not shown during creation of the ticket (it will still be present on previously created tickets) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[TicketTypeAttribute]] + Ticket Type Attribute updated + """ + _response = await self._client_wrapper.httpx_client.request( + f"ticket_types/{jsonable_encoder(ticket_type_id)}/attributes/{jsonable_encoder(id)}", + method="PUT", + json={ + "name": name, + "description": description, + "required_to_create": required_to_create, + "required_to_create_for_contacts": required_to_create_for_contacts, + "visible_on_create": visible_on_create, + "visible_to_contacts": visible_to_contacts, + "multiline": multiline, + "list_items": list_items, + "allow_multiple_values": allow_multiple_values, + "archived": archived, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[TicketTypeAttribute], + construct_type( + type_=typing.Optional[TicketTypeAttribute], # 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) diff --git a/src/intercom/unstable/ticket_type_attributes/types/__init__.py b/src/intercom/unstable/ticket_type_attributes/types/__init__.py new file mode 100644 index 00000000..2670a4cf --- /dev/null +++ b/src/intercom/unstable/ticket_type_attributes/types/__init__.py @@ -0,0 +1,36 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .create_ticket_type_attribute_request_data_type import CreateTicketTypeAttributeRequestDataType +_dynamic_imports: typing.Dict[str, str] = { + "CreateTicketTypeAttributeRequestDataType": ".create_ticket_type_attribute_request_data_type" +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["CreateTicketTypeAttributeRequestDataType"] diff --git a/src/intercom/unstable/ticket_type_attributes/types/create_ticket_type_attribute_request_data_type.py b/src/intercom/unstable/ticket_type_attributes/types/create_ticket_type_attribute_request_data_type.py new file mode 100644 index 00000000..af58adf3 --- /dev/null +++ b/src/intercom/unstable/ticket_type_attributes/types/create_ticket_type_attribute_request_data_type.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CreateTicketTypeAttributeRequestDataType = typing.Union[ + typing.Literal["string", "list", "integer", "decimal", "boolean", "datetime", "files"], typing.Any +] diff --git a/src/intercom/unstable/ticket_types/__init__.py b/src/intercom/unstable/ticket_types/__init__.py new file mode 100644 index 00000000..5cde0202 --- /dev/null +++ b/src/intercom/unstable/ticket_types/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/src/intercom/unstable/ticket_types/client.py b/src/intercom/unstable/ticket_types/client.py new file mode 100644 index 00000000..e6ec477b --- /dev/null +++ b/src/intercom/unstable/ticket_types/client.py @@ -0,0 +1,258 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..tickets.types.ticket_type import TicketType +from ..types.ticket_type_list import TicketTypeList +from .raw_client import AsyncRawTicketTypesClient, RawTicketTypesClient + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class TicketTypesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawTicketTypesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawTicketTypesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawTicketTypesClient + """ + return self._raw_client + + def list_ticket_types(self, *, request_options: typing.Optional[RequestOptions] = None) -> TicketTypeList: + """ + You can get a list of all ticket types for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TicketTypeList + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.ticket_types.list_ticket_types() + """ + _response = self._raw_client.list_ticket_types(request_options=request_options) + return _response.data + + def create_ticket_type( + self, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[TicketType]: + """ + You can create a new ticket type. + > 📘 Creating ticket types. + > + > Every ticket type will be created with two default attributes: _default_title_ and _default_description_. + > For the `icon` propery, use an emoji from [Twemoji Cheatsheet](https://twemoji-cheatsheet.vercel.app/) + + Parameters + ---------- + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[TicketType] + Ticket type created + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.ticket_types.create_ticket_type( + request={"key": "value"}, + ) + """ + _response = self._raw_client.create_ticket_type(request=request, request_options=request_options) + return _response.data + + def get_ticket_type( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[TicketType]: + """ + You can fetch the details of a single ticket type. + + Parameters + ---------- + id : str + The unique identifier for the ticket type which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[TicketType] + Ticket type found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.ticket_types.get_ticket_type( + id="id", + ) + """ + _response = self._raw_client.get_ticket_type(id, request_options=request_options) + return _response.data + + +class AsyncTicketTypesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawTicketTypesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawTicketTypesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawTicketTypesClient + """ + return self._raw_client + + async def list_ticket_types(self, *, request_options: typing.Optional[RequestOptions] = None) -> TicketTypeList: + """ + You can get a list of all ticket types for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TicketTypeList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.ticket_types.list_ticket_types() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list_ticket_types(request_options=request_options) + return _response.data + + async def create_ticket_type( + self, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[TicketType]: + """ + You can create a new ticket type. + > 📘 Creating ticket types. + > + > Every ticket type will be created with two default attributes: _default_title_ and _default_description_. + > For the `icon` propery, use an emoji from [Twemoji Cheatsheet](https://twemoji-cheatsheet.vercel.app/) + + Parameters + ---------- + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[TicketType] + Ticket type created + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.ticket_types.create_ticket_type( + request={"key": "value"}, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create_ticket_type(request=request, request_options=request_options) + return _response.data + + async def get_ticket_type( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[TicketType]: + """ + You can fetch the details of a single ticket type. + + Parameters + ---------- + id : str + The unique identifier for the ticket type which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[TicketType] + Ticket type found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.ticket_types.get_ticket_type( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_ticket_type(id, request_options=request_options) + return _response.data diff --git a/src/intercom/unstable/ticket_types/raw_client.py b/src/intercom/unstable/ticket_types/raw_client.py new file mode 100644 index 00000000..40774c5b --- /dev/null +++ b/src/intercom/unstable/ticket_types/raw_client.py @@ -0,0 +1,346 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ..errors.unauthorized_error import UnauthorizedError +from ..tickets.types.ticket_type import TicketType +from ..types.error import Error +from ..types.ticket_type_list import TicketTypeList + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawTicketTypesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list_ticket_types( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[TicketTypeList]: + """ + You can get a list of all ticket types for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[TicketTypeList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "ticket_types", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TicketTypeList, + construct_type( + type_=TicketTypeList, # 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 create_ticket_type( + self, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[typing.Optional[TicketType]]: + """ + You can create a new ticket type. + > 📘 Creating ticket types. + > + > Every ticket type will be created with two default attributes: _default_title_ and _default_description_. + > For the `icon` propery, use an emoji from [Twemoji Cheatsheet](https://twemoji-cheatsheet.vercel.app/) + + Parameters + ---------- + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[TicketType]] + Ticket type created + """ + _response = self._client_wrapper.httpx_client.request( + "ticket_types", + method="POST", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[TicketType], + construct_type( + type_=typing.Optional[TicketType], # 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 get_ticket_type( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[typing.Optional[TicketType]]: + """ + You can fetch the details of a single ticket type. + + Parameters + ---------- + id : str + The unique identifier for the ticket type which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[TicketType]] + Ticket type found + """ + _response = self._client_wrapper.httpx_client.request( + f"ticket_types/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[TicketType], + construct_type( + type_=typing.Optional[TicketType], # 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) + + +class AsyncRawTicketTypesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list_ticket_types( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[TicketTypeList]: + """ + You can get a list of all ticket types for a workspace. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[TicketTypeList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "ticket_types", + method="GET", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TicketTypeList, + construct_type( + type_=TicketTypeList, # 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 create_ticket_type( + self, *, request: typing.Any, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[typing.Optional[TicketType]]: + """ + You can create a new ticket type. + > 📘 Creating ticket types. + > + > Every ticket type will be created with two default attributes: _default_title_ and _default_description_. + > For the `icon` propery, use an emoji from [Twemoji Cheatsheet](https://twemoji-cheatsheet.vercel.app/) + + Parameters + ---------- + request : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[TicketType]] + Ticket type created + """ + _response = await self._client_wrapper.httpx_client.request( + "ticket_types", + method="POST", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[TicketType], + construct_type( + type_=typing.Optional[TicketType], # 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 get_ticket_type( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[typing.Optional[TicketType]]: + """ + You can fetch the details of a single ticket type. + + Parameters + ---------- + id : str + The unique identifier for the ticket type which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[TicketType]] + Ticket type found + """ + _response = await self._client_wrapper.httpx_client.request( + f"ticket_types/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[TicketType], + construct_type( + type_=typing.Optional[TicketType], # 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) diff --git a/src/intercom/unstable/tickets/__init__.py b/src/intercom/unstable/tickets/__init__.py new file mode 100644 index 00000000..ef820ac3 --- /dev/null +++ b/src/intercom/unstable/tickets/__init__.py @@ -0,0 +1,100 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + DeleteTicketResponse, + ReplyTicketRequestBody, + Ticket, + TicketCategory, + TicketContacts, + TicketPart, + TicketPartPreviousTicketState, + TicketPartTicketState, + TicketPartUpdatedAttributeData, + TicketPartUpdatedAttributeDataAttribute, + TicketPartUpdatedAttributeDataValue, + TicketPartUpdatedAttributeDataValueId, + TicketPartUpdatedAttributeDataValueLabel, + TicketState, + TicketStateCategory, + TicketStateDetailed, + TicketStateDetailedCategory, + TicketStateDetailedTicketTypes, + TicketType, + TicketTypeCategory, + TicketTypeTicketStates, + ) +_dynamic_imports: typing.Dict[str, str] = { + "DeleteTicketResponse": ".types", + "ReplyTicketRequestBody": ".types", + "Ticket": ".types", + "TicketCategory": ".types", + "TicketContacts": ".types", + "TicketPart": ".types", + "TicketPartPreviousTicketState": ".types", + "TicketPartTicketState": ".types", + "TicketPartUpdatedAttributeData": ".types", + "TicketPartUpdatedAttributeDataAttribute": ".types", + "TicketPartUpdatedAttributeDataValue": ".types", + "TicketPartUpdatedAttributeDataValueId": ".types", + "TicketPartUpdatedAttributeDataValueLabel": ".types", + "TicketState": ".types", + "TicketStateCategory": ".types", + "TicketStateDetailed": ".types", + "TicketStateDetailedCategory": ".types", + "TicketStateDetailedTicketTypes": ".types", + "TicketType": ".types", + "TicketTypeCategory": ".types", + "TicketTypeTicketStates": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "DeleteTicketResponse", + "ReplyTicketRequestBody", + "Ticket", + "TicketCategory", + "TicketContacts", + "TicketPart", + "TicketPartPreviousTicketState", + "TicketPartTicketState", + "TicketPartUpdatedAttributeData", + "TicketPartUpdatedAttributeDataAttribute", + "TicketPartUpdatedAttributeDataValue", + "TicketPartUpdatedAttributeDataValueId", + "TicketPartUpdatedAttributeDataValueLabel", + "TicketState", + "TicketStateCategory", + "TicketStateDetailed", + "TicketStateDetailedCategory", + "TicketStateDetailedTicketTypes", + "TicketType", + "TicketTypeCategory", + "TicketTypeTicketStates", +] diff --git a/src/intercom/unstable/tickets/client.py b/src/intercom/unstable/tickets/client.py new file mode 100644 index 00000000..e801336a --- /dev/null +++ b/src/intercom/unstable/tickets/client.py @@ -0,0 +1,890 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ..jobs.types.jobs import Jobs +from ..types.create_ticket_request_assignment import CreateTicketRequestAssignment +from ..types.create_ticket_request_contacts_item import CreateTicketRequestContactsItem +from ..types.search_request_query import SearchRequestQuery +from ..types.starting_after_paging import StartingAfterPaging +from ..types.ticket_list import TicketList +from ..types.ticket_reply import TicketReply +from .raw_client import AsyncRawTicketsClient, RawTicketsClient +from .types.delete_ticket_response import DeleteTicketResponse +from .types.reply_ticket_request_body import ReplyTicketRequestBody +from .types.ticket import Ticket + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class TicketsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawTicketsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawTicketsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawTicketsClient + """ + return self._raw_client + + def reply_ticket( + self, id: str, *, request: ReplyTicketRequestBody, request_options: typing.Optional[RequestOptions] = None + ) -> TicketReply: + """ + You can reply to a ticket with a message from an admin or on behalf of a contact, or with a note for admins. + + Parameters + ---------- + id : str + + request : ReplyTicketRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TicketReply + Admin Reply to send Quick Reply Options + + Examples + -------- + from intercom import Intercom + from intercom.unstable import ContactReplyTicketIntercomUserIdRequest + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.tickets.reply_ticket( + id="123", + request=ContactReplyTicketIntercomUserIdRequest( + body="Thanks again :)", + intercom_user_id="6762f2971bb69f9f2193bc49", + ), + ) + """ + _response = self._raw_client.reply_ticket(id, request=request, request_options=request_options) + return _response.data + + def enqueue_create_ticket( + self, + *, + ticket_type_id: str, + contacts: typing.Sequence[CreateTicketRequestContactsItem], + skip_notifications: typing.Optional[bool] = OMIT, + conversation_to_link_id: typing.Optional[str] = OMIT, + company_id: typing.Optional[str] = OMIT, + created_at: typing.Optional[int] = OMIT, + assignment: typing.Optional[CreateTicketRequestAssignment] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Jobs: + """ + Enqueues ticket creation for asynchronous processing, returning if the job was enqueued successfully to be processed. We attempt to perform a best-effort validation on inputs before tasks are enqueued. If the given parameters are incorrect, we won't enqueue the job. + + Parameters + ---------- + ticket_type_id : str + The ID of the type of ticket you want to create + + contacts : typing.Sequence[CreateTicketRequestContactsItem] + The list of contacts (users or leads) affected by this ticket. Currently only one is allowed + + skip_notifications : typing.Optional[bool] + Option to disable notifications when a Ticket is created. + + conversation_to_link_id : typing.Optional[str] + The ID of the conversation you want to link to the ticket. Here are the valid ways of linking two tickets: + - conversation | back-office ticket + - customer tickets | non-shared back-office ticket + - conversation | tracker ticket + - customer ticket | tracker ticket + + company_id : typing.Optional[str] + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom + + created_at : typing.Optional[int] + The time the ticket was created. If not provided, the current time will be used. + + assignment : typing.Optional[CreateTicketRequestAssignment] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Jobs + Successful response + + Examples + -------- + from intercom import Intercom + from intercom.unstable import CreateTicketRequestContactsItemId + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.tickets.enqueue_create_ticket( + ticket_type_id="1234", + contacts=[ + CreateTicketRequestContactsItemId( + id="6762f2d81bb69f9f2193bc54", + ) + ], + ) + """ + _response = self._raw_client.enqueue_create_ticket( + ticket_type_id=ticket_type_id, + contacts=contacts, + skip_notifications=skip_notifications, + conversation_to_link_id=conversation_to_link_id, + company_id=company_id, + created_at=created_at, + assignment=assignment, + request_options=request_options, + ) + return _response.data + + def get_ticket( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[Ticket]: + """ + You can fetch the details of a single ticket. + + Parameters + ---------- + id : str + The unique identifier for the ticket which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Ticket] + Ticket found + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.tickets.get_ticket( + id="id", + ) + """ + _response = self._raw_client.get_ticket(id, request_options=request_options) + return _response.data + + def update_ticket( + self, + id: str, + *, + ticket_attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + ticket_state_id: typing.Optional[str] = OMIT, + company_id: typing.Optional[str] = OMIT, + open: typing.Optional[bool] = OMIT, + is_shared: typing.Optional[bool] = OMIT, + snoozed_until: typing.Optional[int] = OMIT, + admin_id: typing.Optional[int] = OMIT, + assignee_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[Ticket]: + """ + You can update a ticket. + + Parameters + ---------- + id : str + The unique identifier for the ticket which is given by Intercom + + ticket_attributes : typing.Optional[typing.Dict[str, typing.Any]] + The attributes set on the ticket. + + ticket_state_id : typing.Optional[str] + The ID of the ticket state associated with the ticket type. + + company_id : typing.Optional[str] + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom. Set to nil to remove company. + + open : typing.Optional[bool] + Specify if a ticket is open. Set to false to close a ticket. Closing a ticket will also unsnooze it. + + is_shared : typing.Optional[bool] + Specify whether the ticket is visible to users. + + snoozed_until : typing.Optional[int] + The time you want the ticket to reopen. + + admin_id : typing.Optional[int] + The ID of the admin performing ticket update. Needed for workflows execution and attributing actions to specific admins. + + assignee_id : typing.Optional[str] + The ID of the admin or team to which the ticket is assigned. Set this 0 to unassign it. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Ticket] + Successful response + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.tickets.update_ticket( + id="id", + ticket_attributes={ + "_default_title_": "example", + "_default_description_": "there is a problem", + }, + ticket_state_id="123", + open=True, + snoozed_until=1673609604, + admin_id=991268011, + assignee_id="123", + ) + """ + _response = self._raw_client.update_ticket( + id, + ticket_attributes=ticket_attributes, + ticket_state_id=ticket_state_id, + company_id=company_id, + open=open, + is_shared=is_shared, + snoozed_until=snoozed_until, + admin_id=admin_id, + assignee_id=assignee_id, + request_options=request_options, + ) + return _response.data + + def delete_ticket( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeleteTicketResponse: + """ + You can delete a ticket using the Intercom provided ID. + + Parameters + ---------- + id : str + The unique identifier for the ticket which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteTicketResponse + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.tickets.delete_ticket( + id="id", + ) + """ + _response = self._raw_client.delete_ticket(id, request_options=request_options) + return _response.data + + def search_tickets( + self, + *, + query: SearchRequestQuery, + pagination: typing.Optional[StartingAfterPaging] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> TicketList: + """ + You can search for multiple tickets by the value of their attributes in order to fetch exactly which ones you want. + + To search for tickets, you send a `POST` request to `https://api.intercom.io/tickets/search`. + + This will accept a query object in the body which will define your filters. + {% 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 `20` 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 %} + + ### 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 multiples 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 + + ### Accepted Fields + + Most keys listed as part of the Ticket 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 `"foobar"`). + The `source.body` field is unique as the search will not be performed against the entire value, but instead against every element of the value separately. For example, when searching for a conversation with a `"I need support"` body - the query should contain a `=` operator with the value `"support"` for such conversation to be returned. A query with a `=` operator and a `"need support"` value will not yield a result. + + | Field | Type | + | :---------------------------------------- | :--------------------------------------------------------------------------------------- | + | id | String | + | created_at | Date (UNIX timestamp) | + | updated_at | Date (UNIX timestamp) | + | title | String | + | description | String | + | category | String | + | ticket_type_id | String | + | contact_ids | String | + | teammate_ids | String | + | admin_assignee_id | String | + | team_assignee_id | String | + | open | Boolean | + | state | String | + | snoozed_until | Date (UNIX timestamp) | + | ticket_attribute.{id} | String or Boolean or Date (UNIX timestamp) or Float or Integer | + + {% admonition type="info" name="Searching by Category" %} + When searching for tickets by the **`category`** field, specific terms must be used instead of the category names: + * For **Customer** category tickets, use the term `request`. + * For **Back-office** category tickets, use the term `task`. + * For **Tracker** category tickets, use the term `tracker`. + {% /admonition %} + + ### Accepted Operators + + {% admonition type="info" name="Searching based on `created_at`" %} + You may 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 Shortcut for `OR` queries Values most be in Array | + | NIN | All | Not In Shortcut for `OR !` queries Values must be in Array | + | > | Integer Date (UNIX Timestamp) | Greater (or equal) than | + | < | Integer Date (UNIX Timestamp) | Lower (or equal) 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 + ------- + TicketList + successful + + Examples + -------- + from intercom import Intercom + from intercom.unstable import ( + MultipleFilterSearchRequest, + SingleFilterSearchRequest, + StartingAfterPaging, + ) + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.tickets.search_tickets( + query=MultipleFilterSearchRequest( + operator="AND", + value=[ + SingleFilterSearchRequest( + field="created_at", + operator=">", + value="1306054154", + ) + ], + ), + pagination=StartingAfterPaging( + per_page=5, + ), + ) + """ + _response = self._raw_client.search_tickets(query=query, pagination=pagination, request_options=request_options) + return _response.data + + +class AsyncTicketsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawTicketsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawTicketsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawTicketsClient + """ + return self._raw_client + + async def reply_ticket( + self, id: str, *, request: ReplyTicketRequestBody, request_options: typing.Optional[RequestOptions] = None + ) -> TicketReply: + """ + You can reply to a ticket with a message from an admin or on behalf of a contact, or with a note for admins. + + Parameters + ---------- + id : str + + request : ReplyTicketRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TicketReply + Admin Reply to send Quick Reply Options + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.unstable import ContactReplyTicketIntercomUserIdRequest + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.tickets.reply_ticket( + id="123", + request=ContactReplyTicketIntercomUserIdRequest( + body="Thanks again :)", + intercom_user_id="6762f2971bb69f9f2193bc49", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.reply_ticket(id, request=request, request_options=request_options) + return _response.data + + async def enqueue_create_ticket( + self, + *, + ticket_type_id: str, + contacts: typing.Sequence[CreateTicketRequestContactsItem], + skip_notifications: typing.Optional[bool] = OMIT, + conversation_to_link_id: typing.Optional[str] = OMIT, + company_id: typing.Optional[str] = OMIT, + created_at: typing.Optional[int] = OMIT, + assignment: typing.Optional[CreateTicketRequestAssignment] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> Jobs: + """ + Enqueues ticket creation for asynchronous processing, returning if the job was enqueued successfully to be processed. We attempt to perform a best-effort validation on inputs before tasks are enqueued. If the given parameters are incorrect, we won't enqueue the job. + + Parameters + ---------- + ticket_type_id : str + The ID of the type of ticket you want to create + + contacts : typing.Sequence[CreateTicketRequestContactsItem] + The list of contacts (users or leads) affected by this ticket. Currently only one is allowed + + skip_notifications : typing.Optional[bool] + Option to disable notifications when a Ticket is created. + + conversation_to_link_id : typing.Optional[str] + The ID of the conversation you want to link to the ticket. Here are the valid ways of linking two tickets: + - conversation | back-office ticket + - customer tickets | non-shared back-office ticket + - conversation | tracker ticket + - customer ticket | tracker ticket + + company_id : typing.Optional[str] + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom + + created_at : typing.Optional[int] + The time the ticket was created. If not provided, the current time will be used. + + assignment : typing.Optional[CreateTicketRequestAssignment] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Jobs + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.unstable import CreateTicketRequestContactsItemId + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.tickets.enqueue_create_ticket( + ticket_type_id="1234", + contacts=[ + CreateTicketRequestContactsItemId( + id="6762f2d81bb69f9f2193bc54", + ) + ], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.enqueue_create_ticket( + ticket_type_id=ticket_type_id, + contacts=contacts, + skip_notifications=skip_notifications, + conversation_to_link_id=conversation_to_link_id, + company_id=company_id, + created_at=created_at, + assignment=assignment, + request_options=request_options, + ) + return _response.data + + async def get_ticket( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[Ticket]: + """ + You can fetch the details of a single ticket. + + Parameters + ---------- + id : str + The unique identifier for the ticket which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Ticket] + Ticket found + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.tickets.get_ticket( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get_ticket(id, request_options=request_options) + return _response.data + + async def update_ticket( + self, + id: str, + *, + ticket_attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + ticket_state_id: typing.Optional[str] = OMIT, + company_id: typing.Optional[str] = OMIT, + open: typing.Optional[bool] = OMIT, + is_shared: typing.Optional[bool] = OMIT, + snoozed_until: typing.Optional[int] = OMIT, + admin_id: typing.Optional[int] = OMIT, + assignee_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Optional[Ticket]: + """ + You can update a ticket. + + Parameters + ---------- + id : str + The unique identifier for the ticket which is given by Intercom + + ticket_attributes : typing.Optional[typing.Dict[str, typing.Any]] + The attributes set on the ticket. + + ticket_state_id : typing.Optional[str] + The ID of the ticket state associated with the ticket type. + + company_id : typing.Optional[str] + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom. Set to nil to remove company. + + open : typing.Optional[bool] + Specify if a ticket is open. Set to false to close a ticket. Closing a ticket will also unsnooze it. + + is_shared : typing.Optional[bool] + Specify whether the ticket is visible to users. + + snoozed_until : typing.Optional[int] + The time you want the ticket to reopen. + + admin_id : typing.Optional[int] + The ID of the admin performing ticket update. Needed for workflows execution and attributing actions to specific admins. + + assignee_id : typing.Optional[str] + The ID of the admin or team to which the ticket is assigned. Set this 0 to unassign it. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Ticket] + Successful response + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.tickets.update_ticket( + id="id", + ticket_attributes={ + "_default_title_": "example", + "_default_description_": "there is a problem", + }, + ticket_state_id="123", + open=True, + snoozed_until=1673609604, + admin_id=991268011, + assignee_id="123", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update_ticket( + id, + ticket_attributes=ticket_attributes, + ticket_state_id=ticket_state_id, + company_id=company_id, + open=open, + is_shared=is_shared, + snoozed_until=snoozed_until, + admin_id=admin_id, + assignee_id=assignee_id, + request_options=request_options, + ) + return _response.data + + async def delete_ticket( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> DeleteTicketResponse: + """ + You can delete a ticket using the Intercom provided ID. + + Parameters + ---------- + id : str + The unique identifier for the ticket which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + DeleteTicketResponse + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.tickets.delete_ticket( + id="id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_ticket(id, request_options=request_options) + return _response.data + + async def search_tickets( + self, + *, + query: SearchRequestQuery, + pagination: typing.Optional[StartingAfterPaging] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> TicketList: + """ + You can search for multiple tickets by the value of their attributes in order to fetch exactly which ones you want. + + To search for tickets, you send a `POST` request to `https://api.intercom.io/tickets/search`. + + This will accept a query object in the body which will define your filters. + {% 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 `20` 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 %} + + ### 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 multiples 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 + + ### Accepted Fields + + Most keys listed as part of the Ticket 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 `"foobar"`). + The `source.body` field is unique as the search will not be performed against the entire value, but instead against every element of the value separately. For example, when searching for a conversation with a `"I need support"` body - the query should contain a `=` operator with the value `"support"` for such conversation to be returned. A query with a `=` operator and a `"need support"` value will not yield a result. + + | Field | Type | + | :---------------------------------------- | :--------------------------------------------------------------------------------------- | + | id | String | + | created_at | Date (UNIX timestamp) | + | updated_at | Date (UNIX timestamp) | + | title | String | + | description | String | + | category | String | + | ticket_type_id | String | + | contact_ids | String | + | teammate_ids | String | + | admin_assignee_id | String | + | team_assignee_id | String | + | open | Boolean | + | state | String | + | snoozed_until | Date (UNIX timestamp) | + | ticket_attribute.{id} | String or Boolean or Date (UNIX timestamp) or Float or Integer | + + {% admonition type="info" name="Searching by Category" %} + When searching for tickets by the **`category`** field, specific terms must be used instead of the category names: + * For **Customer** category tickets, use the term `request`. + * For **Back-office** category tickets, use the term `task`. + * For **Tracker** category tickets, use the term `tracker`. + {% /admonition %} + + ### Accepted Operators + + {% admonition type="info" name="Searching based on `created_at`" %} + You may 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 Shortcut for `OR` queries Values most be in Array | + | NIN | All | Not In Shortcut for `OR !` queries Values must be in Array | + | > | Integer Date (UNIX Timestamp) | Greater (or equal) than | + | < | Integer Date (UNIX Timestamp) | Lower (or equal) 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 + ------- + TicketList + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.unstable import ( + MultipleFilterSearchRequest, + SingleFilterSearchRequest, + StartingAfterPaging, + ) + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.tickets.search_tickets( + query=MultipleFilterSearchRequest( + operator="AND", + value=[ + SingleFilterSearchRequest( + field="created_at", + operator=">", + value="1306054154", + ) + ], + ), + pagination=StartingAfterPaging( + per_page=5, + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.search_tickets( + query=query, pagination=pagination, request_options=request_options + ) + return _response.data diff --git a/src/intercom/unstable/tickets/raw_client.py b/src/intercom/unstable/tickets/raw_client.py new file mode 100644 index 00000000..73daa1c4 --- /dev/null +++ b/src/intercom/unstable/tickets/raw_client.py @@ -0,0 +1,1135 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +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.request_options import RequestOptions +from ...core.serialization import convert_and_respect_annotation_metadata +from ...core.unchecked_base_model import construct_type +from ..errors.bad_request_error import BadRequestError +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..jobs.types.jobs import Jobs +from ..types.create_ticket_request_assignment import CreateTicketRequestAssignment +from ..types.create_ticket_request_contacts_item import CreateTicketRequestContactsItem +from ..types.error import Error +from ..types.search_request_query import SearchRequestQuery +from ..types.starting_after_paging import StartingAfterPaging +from ..types.ticket_list import TicketList +from ..types.ticket_reply import TicketReply +from .types.delete_ticket_response import DeleteTicketResponse +from .types.reply_ticket_request_body import ReplyTicketRequestBody +from .types.ticket import Ticket + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawTicketsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def reply_ticket( + self, id: str, *, request: ReplyTicketRequestBody, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[TicketReply]: + """ + You can reply to a ticket with a message from an admin or on behalf of a contact, or with a note for admins. + + Parameters + ---------- + id : str + + request : ReplyTicketRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[TicketReply] + Admin Reply to send Quick Reply Options + """ + _response = self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(id)}/reply", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=ReplyTicketRequestBody, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TicketReply, + construct_type( + type_=TicketReply, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 enqueue_create_ticket( + self, + *, + ticket_type_id: str, + contacts: typing.Sequence[CreateTicketRequestContactsItem], + skip_notifications: typing.Optional[bool] = OMIT, + conversation_to_link_id: typing.Optional[str] = OMIT, + company_id: typing.Optional[str] = OMIT, + created_at: typing.Optional[int] = OMIT, + assignment: typing.Optional[CreateTicketRequestAssignment] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Jobs]: + """ + Enqueues ticket creation for asynchronous processing, returning if the job was enqueued successfully to be processed. We attempt to perform a best-effort validation on inputs before tasks are enqueued. If the given parameters are incorrect, we won't enqueue the job. + + Parameters + ---------- + ticket_type_id : str + The ID of the type of ticket you want to create + + contacts : typing.Sequence[CreateTicketRequestContactsItem] + The list of contacts (users or leads) affected by this ticket. Currently only one is allowed + + skip_notifications : typing.Optional[bool] + Option to disable notifications when a Ticket is created. + + conversation_to_link_id : typing.Optional[str] + The ID of the conversation you want to link to the ticket. Here are the valid ways of linking two tickets: + - conversation | back-office ticket + - customer tickets | non-shared back-office ticket + - conversation | tracker ticket + - customer ticket | tracker ticket + + company_id : typing.Optional[str] + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom + + created_at : typing.Optional[int] + The time the ticket was created. If not provided, the current time will be used. + + assignment : typing.Optional[CreateTicketRequestAssignment] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Jobs] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + "tickets/enqueue", + method="POST", + json={ + "skip_notifications": skip_notifications, + "ticket_type_id": ticket_type_id, + "contacts": convert_and_respect_annotation_metadata( + object_=contacts, annotation=typing.Sequence[CreateTicketRequestContactsItem], direction="write" + ), + "conversation_to_link_id": conversation_to_link_id, + "company_id": company_id, + "created_at": created_at, + "assignment": convert_and_respect_annotation_metadata( + object_=assignment, annotation=CreateTicketRequestAssignment, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Jobs, + construct_type( + type_=Jobs, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 get_ticket( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[typing.Optional[Ticket]]: + """ + You can fetch the details of a single ticket. + + Parameters + ---------- + id : str + The unique identifier for the ticket which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[Ticket]] + Ticket found + """ + _response = self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Ticket], + construct_type( + type_=typing.Optional[Ticket], # 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_ticket( + self, + id: str, + *, + ticket_attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + ticket_state_id: typing.Optional[str] = OMIT, + company_id: typing.Optional[str] = OMIT, + open: typing.Optional[bool] = OMIT, + is_shared: typing.Optional[bool] = OMIT, + snoozed_until: typing.Optional[int] = OMIT, + admin_id: typing.Optional[int] = OMIT, + assignee_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[typing.Optional[Ticket]]: + """ + You can update a ticket. + + Parameters + ---------- + id : str + The unique identifier for the ticket which is given by Intercom + + ticket_attributes : typing.Optional[typing.Dict[str, typing.Any]] + The attributes set on the ticket. + + ticket_state_id : typing.Optional[str] + The ID of the ticket state associated with the ticket type. + + company_id : typing.Optional[str] + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom. Set to nil to remove company. + + open : typing.Optional[bool] + Specify if a ticket is open. Set to false to close a ticket. Closing a ticket will also unsnooze it. + + is_shared : typing.Optional[bool] + Specify whether the ticket is visible to users. + + snoozed_until : typing.Optional[int] + The time you want the ticket to reopen. + + admin_id : typing.Optional[int] + The ID of the admin performing ticket update. Needed for workflows execution and attributing actions to specific admins. + + assignee_id : typing.Optional[str] + The ID of the admin or team to which the ticket is assigned. Set this 0 to unassign it. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[Ticket]] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(id)}", + method="PUT", + json={ + "ticket_attributes": ticket_attributes, + "ticket_state_id": ticket_state_id, + "company_id": company_id, + "open": open, + "is_shared": is_shared, + "snoozed_until": snoozed_until, + "admin_id": admin_id, + "assignee_id": assignee_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Ticket], + construct_type( + type_=typing.Optional[Ticket], # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 delete_ticket( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[DeleteTicketResponse]: + """ + You can delete a ticket using the Intercom provided ID. + + Parameters + ---------- + id : str + The unique identifier for the ticket which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[DeleteTicketResponse] + successful + """ + _response = self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteTicketResponse, + construct_type( + type_=DeleteTicketResponse, # 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 search_tickets( + self, + *, + query: SearchRequestQuery, + pagination: typing.Optional[StartingAfterPaging] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[TicketList]: + """ + You can search for multiple tickets by the value of their attributes in order to fetch exactly which ones you want. + + To search for tickets, you send a `POST` request to `https://api.intercom.io/tickets/search`. + + This will accept a query object in the body which will define your filters. + {% 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 `20` 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 %} + + ### 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 multiples 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 + + ### Accepted Fields + + Most keys listed as part of the Ticket 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 `"foobar"`). + The `source.body` field is unique as the search will not be performed against the entire value, but instead against every element of the value separately. For example, when searching for a conversation with a `"I need support"` body - the query should contain a `=` operator with the value `"support"` for such conversation to be returned. A query with a `=` operator and a `"need support"` value will not yield a result. + + | Field | Type | + | :---------------------------------------- | :--------------------------------------------------------------------------------------- | + | id | String | + | created_at | Date (UNIX timestamp) | + | updated_at | Date (UNIX timestamp) | + | title | String | + | description | String | + | category | String | + | ticket_type_id | String | + | contact_ids | String | + | teammate_ids | String | + | admin_assignee_id | String | + | team_assignee_id | String | + | open | Boolean | + | state | String | + | snoozed_until | Date (UNIX timestamp) | + | ticket_attribute.{id} | String or Boolean or Date (UNIX timestamp) or Float or Integer | + + {% admonition type="info" name="Searching by Category" %} + When searching for tickets by the **`category`** field, specific terms must be used instead of the category names: + * For **Customer** category tickets, use the term `request`. + * For **Back-office** category tickets, use the term `task`. + * For **Tracker** category tickets, use the term `tracker`. + {% /admonition %} + + ### Accepted Operators + + {% admonition type="info" name="Searching based on `created_at`" %} + You may 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 Shortcut for `OR` queries Values most be in Array | + | NIN | All | Not In Shortcut for `OR !` queries Values must be in Array | + | > | Integer Date (UNIX Timestamp) | Greater (or equal) than | + | < | Integer Date (UNIX Timestamp) | Lower (or equal) 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 + ------- + HttpResponse[TicketList] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "tickets/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: + _data = typing.cast( + TicketList, + construct_type( + type_=TicketList, # 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 AsyncRawTicketsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def reply_ticket( + self, id: str, *, request: ReplyTicketRequestBody, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[TicketReply]: + """ + You can reply to a ticket with a message from an admin or on behalf of a contact, or with a note for admins. + + Parameters + ---------- + id : str + + request : ReplyTicketRequestBody + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[TicketReply] + Admin Reply to send Quick Reply Options + """ + _response = await self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(id)}/reply", + method="POST", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=ReplyTicketRequestBody, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + TicketReply, + construct_type( + type_=TicketReply, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 enqueue_create_ticket( + self, + *, + ticket_type_id: str, + contacts: typing.Sequence[CreateTicketRequestContactsItem], + skip_notifications: typing.Optional[bool] = OMIT, + conversation_to_link_id: typing.Optional[str] = OMIT, + company_id: typing.Optional[str] = OMIT, + created_at: typing.Optional[int] = OMIT, + assignment: typing.Optional[CreateTicketRequestAssignment] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Jobs]: + """ + Enqueues ticket creation for asynchronous processing, returning if the job was enqueued successfully to be processed. We attempt to perform a best-effort validation on inputs before tasks are enqueued. If the given parameters are incorrect, we won't enqueue the job. + + Parameters + ---------- + ticket_type_id : str + The ID of the type of ticket you want to create + + contacts : typing.Sequence[CreateTicketRequestContactsItem] + The list of contacts (users or leads) affected by this ticket. Currently only one is allowed + + skip_notifications : typing.Optional[bool] + Option to disable notifications when a Ticket is created. + + conversation_to_link_id : typing.Optional[str] + The ID of the conversation you want to link to the ticket. Here are the valid ways of linking two tickets: + - conversation | back-office ticket + - customer tickets | non-shared back-office ticket + - conversation | tracker ticket + - customer ticket | tracker ticket + + company_id : typing.Optional[str] + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom + + created_at : typing.Optional[int] + The time the ticket was created. If not provided, the current time will be used. + + assignment : typing.Optional[CreateTicketRequestAssignment] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Jobs] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + "tickets/enqueue", + method="POST", + json={ + "skip_notifications": skip_notifications, + "ticket_type_id": ticket_type_id, + "contacts": convert_and_respect_annotation_metadata( + object_=contacts, annotation=typing.Sequence[CreateTicketRequestContactsItem], direction="write" + ), + "conversation_to_link_id": conversation_to_link_id, + "company_id": company_id, + "created_at": created_at, + "assignment": convert_and_respect_annotation_metadata( + object_=assignment, annotation=CreateTicketRequestAssignment, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Jobs, + construct_type( + type_=Jobs, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 get_ticket( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[typing.Optional[Ticket]]: + """ + You can fetch the details of a single ticket. + + Parameters + ---------- + id : str + The unique identifier for the ticket which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[Ticket]] + Ticket found + """ + _response = await self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(id)}", + method="GET", + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Ticket], + construct_type( + type_=typing.Optional[Ticket], # 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_ticket( + self, + id: str, + *, + ticket_attributes: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + ticket_state_id: typing.Optional[str] = OMIT, + company_id: typing.Optional[str] = OMIT, + open: typing.Optional[bool] = OMIT, + is_shared: typing.Optional[bool] = OMIT, + snoozed_until: typing.Optional[int] = OMIT, + admin_id: typing.Optional[int] = OMIT, + assignee_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[typing.Optional[Ticket]]: + """ + You can update a ticket. + + Parameters + ---------- + id : str + The unique identifier for the ticket which is given by Intercom + + ticket_attributes : typing.Optional[typing.Dict[str, typing.Any]] + The attributes set on the ticket. + + ticket_state_id : typing.Optional[str] + The ID of the ticket state associated with the ticket type. + + company_id : typing.Optional[str] + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom. Set to nil to remove company. + + open : typing.Optional[bool] + Specify if a ticket is open. Set to false to close a ticket. Closing a ticket will also unsnooze it. + + is_shared : typing.Optional[bool] + Specify whether the ticket is visible to users. + + snoozed_until : typing.Optional[int] + The time you want the ticket to reopen. + + admin_id : typing.Optional[int] + The ID of the admin performing ticket update. Needed for workflows execution and attributing actions to specific admins. + + assignee_id : typing.Optional[str] + The ID of the admin or team to which the ticket is assigned. Set this 0 to unassign it. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[Ticket]] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(id)}", + method="PUT", + json={ + "ticket_attributes": ticket_attributes, + "ticket_state_id": ticket_state_id, + "company_id": company_id, + "open": open, + "is_shared": is_shared, + "snoozed_until": snoozed_until, + "admin_id": admin_id, + "assignee_id": assignee_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Ticket], + construct_type( + type_=typing.Optional[Ticket], # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + construct_type( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + 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 delete_ticket( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[DeleteTicketResponse]: + """ + You can delete a ticket using the Intercom provided ID. + + Parameters + ---------- + id : str + The unique identifier for the ticket which is given by Intercom. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[DeleteTicketResponse] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + f"tickets/{jsonable_encoder(id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + DeleteTicketResponse, + construct_type( + type_=DeleteTicketResponse, # 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 search_tickets( + self, + *, + query: SearchRequestQuery, + pagination: typing.Optional[StartingAfterPaging] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[TicketList]: + """ + You can search for multiple tickets by the value of their attributes in order to fetch exactly which ones you want. + + To search for tickets, you send a `POST` request to `https://api.intercom.io/tickets/search`. + + This will accept a query object in the body which will define your filters. + {% 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 `20` 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 %} + + ### 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 multiples 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 + + ### Accepted Fields + + Most keys listed as part of the Ticket 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 `"foobar"`). + The `source.body` field is unique as the search will not be performed against the entire value, but instead against every element of the value separately. For example, when searching for a conversation with a `"I need support"` body - the query should contain a `=` operator with the value `"support"` for such conversation to be returned. A query with a `=` operator and a `"need support"` value will not yield a result. + + | Field | Type | + | :---------------------------------------- | :--------------------------------------------------------------------------------------- | + | id | String | + | created_at | Date (UNIX timestamp) | + | updated_at | Date (UNIX timestamp) | + | title | String | + | description | String | + | category | String | + | ticket_type_id | String | + | contact_ids | String | + | teammate_ids | String | + | admin_assignee_id | String | + | team_assignee_id | String | + | open | Boolean | + | state | String | + | snoozed_until | Date (UNIX timestamp) | + | ticket_attribute.{id} | String or Boolean or Date (UNIX timestamp) or Float or Integer | + + {% admonition type="info" name="Searching by Category" %} + When searching for tickets by the **`category`** field, specific terms must be used instead of the category names: + * For **Customer** category tickets, use the term `request`. + * For **Back-office** category tickets, use the term `task`. + * For **Tracker** category tickets, use the term `tracker`. + {% /admonition %} + + ### Accepted Operators + + {% admonition type="info" name="Searching based on `created_at`" %} + You may 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 Shortcut for `OR` queries Values most be in Array | + | NIN | All | Not In Shortcut for `OR !` queries Values must be in Array | + | > | Integer Date (UNIX Timestamp) | Greater (or equal) than | + | < | Integer Date (UNIX Timestamp) | Lower (or equal) 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 + ------- + AsyncHttpResponse[TicketList] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "tickets/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: + _data = typing.cast( + TicketList, + construct_type( + type_=TicketList, # 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) diff --git a/src/intercom/unstable/tickets/types/__init__.py b/src/intercom/unstable/tickets/types/__init__.py new file mode 100644 index 00000000..afbb8563 --- /dev/null +++ b/src/intercom/unstable/tickets/types/__init__.py @@ -0,0 +1,98 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .delete_ticket_response import DeleteTicketResponse + from .reply_ticket_request_body import ReplyTicketRequestBody + from .ticket import Ticket + from .ticket_category import TicketCategory + from .ticket_contacts import TicketContacts + from .ticket_part import TicketPart + from .ticket_part_previous_ticket_state import TicketPartPreviousTicketState + from .ticket_part_ticket_state import TicketPartTicketState + from .ticket_part_updated_attribute_data import TicketPartUpdatedAttributeData + from .ticket_part_updated_attribute_data_attribute import TicketPartUpdatedAttributeDataAttribute + from .ticket_part_updated_attribute_data_value import TicketPartUpdatedAttributeDataValue + from .ticket_part_updated_attribute_data_value_id import TicketPartUpdatedAttributeDataValueId + from .ticket_part_updated_attribute_data_value_label import TicketPartUpdatedAttributeDataValueLabel + from .ticket_state import TicketState + from .ticket_state_category import TicketStateCategory + from .ticket_state_detailed import TicketStateDetailed + from .ticket_state_detailed_category import TicketStateDetailedCategory + from .ticket_state_detailed_ticket_types import TicketStateDetailedTicketTypes + from .ticket_type import TicketType + from .ticket_type_category import TicketTypeCategory + from .ticket_type_ticket_states import TicketTypeTicketStates +_dynamic_imports: typing.Dict[str, str] = { + "DeleteTicketResponse": ".delete_ticket_response", + "ReplyTicketRequestBody": ".reply_ticket_request_body", + "Ticket": ".ticket", + "TicketCategory": ".ticket_category", + "TicketContacts": ".ticket_contacts", + "TicketPart": ".ticket_part", + "TicketPartPreviousTicketState": ".ticket_part_previous_ticket_state", + "TicketPartTicketState": ".ticket_part_ticket_state", + "TicketPartUpdatedAttributeData": ".ticket_part_updated_attribute_data", + "TicketPartUpdatedAttributeDataAttribute": ".ticket_part_updated_attribute_data_attribute", + "TicketPartUpdatedAttributeDataValue": ".ticket_part_updated_attribute_data_value", + "TicketPartUpdatedAttributeDataValueId": ".ticket_part_updated_attribute_data_value_id", + "TicketPartUpdatedAttributeDataValueLabel": ".ticket_part_updated_attribute_data_value_label", + "TicketState": ".ticket_state", + "TicketStateCategory": ".ticket_state_category", + "TicketStateDetailed": ".ticket_state_detailed", + "TicketStateDetailedCategory": ".ticket_state_detailed_category", + "TicketStateDetailedTicketTypes": ".ticket_state_detailed_ticket_types", + "TicketType": ".ticket_type", + "TicketTypeCategory": ".ticket_type_category", + "TicketTypeTicketStates": ".ticket_type_ticket_states", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "DeleteTicketResponse", + "ReplyTicketRequestBody", + "Ticket", + "TicketCategory", + "TicketContacts", + "TicketPart", + "TicketPartPreviousTicketState", + "TicketPartTicketState", + "TicketPartUpdatedAttributeData", + "TicketPartUpdatedAttributeDataAttribute", + "TicketPartUpdatedAttributeDataValue", + "TicketPartUpdatedAttributeDataValueId", + "TicketPartUpdatedAttributeDataValueLabel", + "TicketState", + "TicketStateCategory", + "TicketStateDetailed", + "TicketStateDetailedCategory", + "TicketStateDetailedTicketTypes", + "TicketType", + "TicketTypeCategory", + "TicketTypeTicketStates", +] diff --git a/src/intercom/unstable/tickets/types/delete_ticket_response.py b/src/intercom/unstable/tickets/types/delete_ticket_response.py new file mode 100644 index 00000000..4c439b33 --- /dev/null +++ b/src/intercom/unstable/tickets/types/delete_ticket_response.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class DeleteTicketResponse(UncheckedBaseModel): + """ + Response when a ticket is deleted. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the ticket. + """ + + object: typing.Optional[typing.Literal["ticket"]] = pydantic.Field(default=None) + """ + always ticket + """ + + deleted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the ticket is deleted or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/tickets/types/reply_ticket_request_body.py b/src/intercom/unstable/tickets/types/reply_ticket_request_body.py new file mode 100644 index 00000000..2c3fcad4 --- /dev/null +++ b/src/intercom/unstable/tickets/types/reply_ticket_request_body.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...types.admin_reply_ticket_request import AdminReplyTicketRequest +from ...types.contact_reply_ticket_request import ContactReplyTicketRequest + +ReplyTicketRequestBody = typing.Union[ContactReplyTicketRequest, AdminReplyTicketRequest] diff --git a/src/intercom/unstable/tickets/types/ticket.py b/src/intercom/unstable/tickets/types/ticket.py new file mode 100644 index 00000000..bcf4b62b --- /dev/null +++ b/src/intercom/unstable/tickets/types/ticket.py @@ -0,0 +1,90 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...types.linked_object_list import LinkedObjectList +from ...types.ticket_custom_attributes import TicketCustomAttributes +from ...types.ticket_parts import TicketParts +from .ticket_category import TicketCategory +from .ticket_contacts import TicketContacts +from .ticket_state import TicketState +from .ticket_type import TicketType + + +class Ticket(UncheckedBaseModel): + """ + Tickets are how you track requests from your users. + """ + + type: typing.Optional[typing.Literal["ticket"]] = pydantic.Field(default=None) + """ + Always ticket + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the ticket which is given by Intercom. + """ + + ticket_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the Ticket used in the Intercom Inbox and Messenger. Do not use ticket_id for API queries. + """ + + category: typing.Optional[TicketCategory] = pydantic.Field(default=None) + """ + Category of the Ticket. + """ + + ticket_attributes: typing.Optional[TicketCustomAttributes] = None + ticket_state: typing.Optional[TicketState] = None + ticket_type: typing.Optional[TicketType] = None + contacts: typing.Optional[TicketContacts] = None + admin_assignee_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the admin assigned to the ticket. + """ + + team_assignee_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the team assigned to the ticket. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the ticket was created as a UTC Unix timestamp. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The last time the ticket was updated as a UTC Unix timestamp. + """ + + open: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether or not the ticket is open. If false, the ticket is closed. + """ + + snoozed_until: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the ticket will be snoozed until as a UTC Unix timestamp. If null, the ticket is not currently snoozed. + """ + + linked_objects: typing.Optional[LinkedObjectList] = None + ticket_parts: typing.Optional[TicketParts] = None + is_shared: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether or not the ticket is shared with the customer. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/tickets/types/ticket_category.py b/src/intercom/unstable/tickets/types/ticket_category.py new file mode 100644 index 00000000..0bfaf71b --- /dev/null +++ b/src/intercom/unstable/tickets/types/ticket_category.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketCategory = typing.Union[typing.Literal["Customer", "Back-office", "Tracker"], typing.Any] diff --git a/src/intercom/unstable/tickets/types/ticket_contacts.py b/src/intercom/unstable/tickets/types/ticket_contacts.py new file mode 100644 index 00000000..8146c4b9 --- /dev/null +++ b/src/intercom/unstable/tickets/types/ticket_contacts.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...types.contact_reference import ContactReference + + +class TicketContacts(UncheckedBaseModel): + """ + The list of contacts affected by a ticket. + """ + + type: typing.Optional[typing.Literal["contact.list"]] = pydantic.Field(default=None) + """ + always contact.list + """ + + contacts: typing.Optional[typing.List[ContactReference]] = pydantic.Field(default=None) + """ + The list of contacts affected by this ticket. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/tickets/types/ticket_part.py b/src/intercom/unstable/tickets/types/ticket_part.py new file mode 100644 index 00000000..b7106de5 --- /dev/null +++ b/src/intercom/unstable/tickets/types/ticket_part.py @@ -0,0 +1,99 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...types.part_attachment import PartAttachment +from ...types.reference import Reference +from ...types.ticket_part_author import TicketPartAuthor +from .ticket_part_previous_ticket_state import TicketPartPreviousTicketState +from .ticket_part_ticket_state import TicketPartTicketState +from .ticket_part_updated_attribute_data import TicketPartUpdatedAttributeData + + +class TicketPart(UncheckedBaseModel): + """ + A Ticket Part represents a message in the ticket. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + Always ticket_part + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the ticket part. + """ + + part_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of ticket part. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The message body, which may contain HTML. + """ + + previous_ticket_state: typing.Optional[TicketPartPreviousTicketState] = pydantic.Field(default=None) + """ + The previous state of the ticket. + """ + + ticket_state: typing.Optional[TicketPartTicketState] = pydantic.Field(default=None) + """ + The state of the ticket. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the ticket part was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The last time the ticket part was updated. + """ + + assigned_to: typing.Optional[Reference] = pydantic.Field(default=None) + """ + The id of the admin that was assigned the ticket by this ticket_part (null if there has been no change in assignment.) + """ + + author: typing.Optional[TicketPartAuthor] = None + attachments: typing.Optional[typing.List[PartAttachment]] = pydantic.Field(default=None) + """ + A list of attachments for the part. + """ + + external_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The external id of the ticket part + """ + + redacted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether or not the ticket part has been redacted. + """ + + app_package_code: typing.Optional[str] = pydantic.Field(default=None) + """ + The app package code if this part was created via API. Note this field won't show if the part was not created via API. + """ + + updated_attribute_data: typing.Optional[TicketPartUpdatedAttributeData] = pydantic.Field(default=None) + """ + The updated attribute data of the ticket part. Only present for attribute update parts. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/tickets/types/ticket_part_previous_ticket_state.py b/src/intercom/unstable/tickets/types/ticket_part_previous_ticket_state.py new file mode 100644 index 00000000..650c92d9 --- /dev/null +++ b/src/intercom/unstable/tickets/types/ticket_part_previous_ticket_state.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketPartPreviousTicketState = typing.Union[ + typing.Literal["submitted", "in_progress", "waiting_on_customer", "resolved"], typing.Any +] diff --git a/src/intercom/unstable/tickets/types/ticket_part_ticket_state.py b/src/intercom/unstable/tickets/types/ticket_part_ticket_state.py new file mode 100644 index 00000000..c430063e --- /dev/null +++ b/src/intercom/unstable/tickets/types/ticket_part_ticket_state.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketPartTicketState = typing.Union[ + typing.Literal["submitted", "in_progress", "waiting_on_customer", "resolved"], typing.Any +] diff --git a/src/intercom/unstable/tickets/types/ticket_part_updated_attribute_data.py b/src/intercom/unstable/tickets/types/ticket_part_updated_attribute_data.py new file mode 100644 index 00000000..3d1b7106 --- /dev/null +++ b/src/intercom/unstable/tickets/types/ticket_part_updated_attribute_data.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .ticket_part_updated_attribute_data_attribute import TicketPartUpdatedAttributeDataAttribute +from .ticket_part_updated_attribute_data_value import TicketPartUpdatedAttributeDataValue + + +class TicketPartUpdatedAttributeData(UncheckedBaseModel): + """ + The updated attribute data of the ticket part. Only present for attribute update parts. + """ + + attribute: TicketPartUpdatedAttributeDataAttribute = pydantic.Field() + """ + Information about the attribute that was updated. + """ + + value: TicketPartUpdatedAttributeDataValue = pydantic.Field() + """ + The new value of the attribute. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/tickets/types/ticket_part_updated_attribute_data_attribute.py b/src/intercom/unstable/tickets/types/ticket_part_updated_attribute_data_attribute.py new file mode 100644 index 00000000..2825ec67 --- /dev/null +++ b/src/intercom/unstable/tickets/types/ticket_part_updated_attribute_data_attribute.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel + + +class TicketPartUpdatedAttributeDataAttribute(UncheckedBaseModel): + """ + Information about the attribute that was updated. + """ + + type: typing.Literal["attribute"] = pydantic.Field(default="attribute") + """ + The type of the object. Always 'attribute'. + """ + + id: str = pydantic.Field() + """ + The unique identifier of the attribute. + """ + + label: str = pydantic.Field() + """ + The human-readable name of the attribute. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/tickets/types/ticket_part_updated_attribute_data_value.py b/src/intercom/unstable/tickets/types/ticket_part_updated_attribute_data_value.py new file mode 100644 index 00000000..d0c6f091 --- /dev/null +++ b/src/intercom/unstable/tickets/types/ticket_part_updated_attribute_data_value.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .ticket_part_updated_attribute_data_value_id import TicketPartUpdatedAttributeDataValueId +from .ticket_part_updated_attribute_data_value_label import TicketPartUpdatedAttributeDataValueLabel + + +class TicketPartUpdatedAttributeDataValue(UncheckedBaseModel): + """ + The new value of the attribute. + """ + + type: typing.Literal["value"] = pydantic.Field(default="value") + """ + The type of the object. Always 'value'. + """ + + id: TicketPartUpdatedAttributeDataValueId + label: TicketPartUpdatedAttributeDataValueLabel + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/tickets/types/ticket_part_updated_attribute_data_value_id.py b/src/intercom/unstable/tickets/types/ticket_part_updated_attribute_data_value_id.py new file mode 100644 index 00000000..01d2f9d1 --- /dev/null +++ b/src/intercom/unstable/tickets/types/ticket_part_updated_attribute_data_value_id.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketPartUpdatedAttributeDataValueId = typing.Union[typing.Optional[str], typing.List[int]] diff --git a/src/intercom/unstable/tickets/types/ticket_part_updated_attribute_data_value_label.py b/src/intercom/unstable/tickets/types/ticket_part_updated_attribute_data_value_label.py new file mode 100644 index 00000000..4026e8af --- /dev/null +++ b/src/intercom/unstable/tickets/types/ticket_part_updated_attribute_data_value_label.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketPartUpdatedAttributeDataValueLabel = typing.Union[str, typing.List[str]] diff --git a/src/intercom/unstable/tickets/types/ticket_state.py b/src/intercom/unstable/tickets/types/ticket_state.py new file mode 100644 index 00000000..67823554 --- /dev/null +++ b/src/intercom/unstable/tickets/types/ticket_state.py @@ -0,0 +1,48 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .ticket_state_category import TicketStateCategory + + +class TicketState(UncheckedBaseModel): + """ + A ticket state, used to define the state of a ticket. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `ticket_state`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the ticket state + """ + + category: typing.Optional[TicketStateCategory] = pydantic.Field(default=None) + """ + The category of the ticket state + """ + + internal_label: typing.Optional[str] = pydantic.Field(default=None) + """ + The state the ticket is currently in, in a human readable form - visible in Intercom + """ + + external_label: typing.Optional[str] = pydantic.Field(default=None) + """ + The state the ticket is currently in, in a human readable form - visible to customers, in the messenger, email and tickets portal. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/tickets/types/ticket_state_category.py b/src/intercom/unstable/tickets/types/ticket_state_category.py new file mode 100644 index 00000000..d53c56a7 --- /dev/null +++ b/src/intercom/unstable/tickets/types/ticket_state_category.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketStateCategory = typing.Union[ + typing.Literal["submitted", "in_progress", "waiting_on_customer", "resolved"], typing.Any +] diff --git a/src/intercom/unstable/tickets/types/ticket_state_detailed.py b/src/intercom/unstable/tickets/types/ticket_state_detailed.py new file mode 100644 index 00000000..c596d929 --- /dev/null +++ b/src/intercom/unstable/tickets/types/ticket_state_detailed.py @@ -0,0 +1,59 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .ticket_state_detailed_category import TicketStateDetailedCategory +from .ticket_state_detailed_ticket_types import TicketStateDetailedTicketTypes + + +class TicketStateDetailed(UncheckedBaseModel): + """ + A ticket state, used to define the state of a ticket. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `ticket_state`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the ticket state + """ + + category: typing.Optional[TicketStateDetailedCategory] = pydantic.Field(default=None) + """ + The category of the ticket state + """ + + internal_label: typing.Optional[str] = pydantic.Field(default=None) + """ + The state the ticket is currently in, in a human readable form - visible in Intercom + """ + + external_label: typing.Optional[str] = pydantic.Field(default=None) + """ + The state the ticket is currently in, in a human readable form - visible to customers, in the messenger, email and tickets portal. + """ + + archived: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the ticket state is archived + """ + + ticket_types: typing.Optional[TicketStateDetailedTicketTypes] = pydantic.Field(default=None) + """ + A list of ticket types associated with a given ticket state. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/tickets/types/ticket_state_detailed_category.py b/src/intercom/unstable/tickets/types/ticket_state_detailed_category.py new file mode 100644 index 00000000..d8426ecc --- /dev/null +++ b/src/intercom/unstable/tickets/types/ticket_state_detailed_category.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketStateDetailedCategory = typing.Union[ + typing.Literal["submitted", "in_progress", "waiting_on_customer", "resolved"], typing.Any +] diff --git a/src/intercom/unstable/tickets/types/ticket_state_detailed_ticket_types.py b/src/intercom/unstable/tickets/types/ticket_state_detailed_ticket_types.py new file mode 100644 index 00000000..9e018f7c --- /dev/null +++ b/src/intercom/unstable/tickets/types/ticket_state_detailed_ticket_types.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .ticket_type import TicketType + + +class TicketStateDetailedTicketTypes(UncheckedBaseModel): + """ + A list of ticket types associated with a given ticket state. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `list`. + """ + + data: typing.Optional[typing.List[typing.Optional[TicketType]]] = pydantic.Field(default=None) + """ + A list of ticket type attributes associated with a given ticket type. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/tickets/types/ticket_type.py b/src/intercom/unstable/tickets/types/ticket_type.py new file mode 100644 index 00000000..c75332bd --- /dev/null +++ b/src/intercom/unstable/tickets/types/ticket_type.py @@ -0,0 +1,81 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from ...types.ticket_type_attribute_list import TicketTypeAttributeList +from .ticket_type_category import TicketTypeCategory +from .ticket_type_ticket_states import TicketTypeTicketStates + + +class TicketType(UncheckedBaseModel): + """ + A ticket type, used to define the data fields to be captured in a ticket. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `ticket_type`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the ticket type. + """ + + category: typing.Optional[TicketTypeCategory] = pydantic.Field(default=None) + """ + Category of the Ticket Type. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the ticket type + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + The description of the ticket type + """ + + icon: typing.Optional[str] = pydantic.Field(default=None) + """ + The icon of the ticket type + """ + + workspace_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the workspace that the ticket type belongs to. + """ + + ticket_type_attributes: typing.Optional[TicketTypeAttributeList] = None + ticket_states: typing.Optional[TicketTypeTicketStates] = pydantic.Field(default=None) + """ + A list of ticket states associated with a given ticket type. + """ + + archived: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the ticket type is archived or not. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The date and time the ticket type was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The date and time the ticket type was last updated. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/tickets/types/ticket_type_category.py b/src/intercom/unstable/tickets/types/ticket_type_category.py new file mode 100644 index 00000000..afbe5d6b --- /dev/null +++ b/src/intercom/unstable/tickets/types/ticket_type_category.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketTypeCategory = typing.Union[typing.Literal["Customer", "Back-office", "Tracker"], typing.Any] diff --git a/src/intercom/unstable/tickets/types/ticket_type_ticket_states.py b/src/intercom/unstable/tickets/types/ticket_type_ticket_states.py new file mode 100644 index 00000000..d4d22c96 --- /dev/null +++ b/src/intercom/unstable/tickets/types/ticket_type_ticket_states.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2 +from ....core.unchecked_base_model import UncheckedBaseModel +from .ticket_state import TicketState + + +class TicketTypeTicketStates(UncheckedBaseModel): + """ + A list of ticket states associated with a given ticket type. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `list`. + """ + + data: typing.Optional[typing.List[typing.Optional[TicketState]]] = pydantic.Field(default=None) + """ + A list of ticket states associated with a given ticket type. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/__init__.py b/src/intercom/unstable/types/__init__.py new file mode 100644 index 00000000..7dfd395a --- /dev/null +++ b/src/intercom/unstable/types/__init__.py @@ -0,0 +1,741 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .activity_log import ActivityLog + from .activity_log_activity_type import ActivityLogActivityType + from .activity_log_list import ActivityLogList + from .activity_log_metadata import ActivityLogMetadata + from .activity_log_performed_by import ActivityLogPerformedBy + from .addressable_list import AddressableList + from .admin_list import AdminList + from .admin_priority_level import AdminPriorityLevel + from .admin_reply_conversation_request import AdminReplyConversationRequest + from .admin_reply_conversation_request_message_type import AdminReplyConversationRequestMessageType + from .admin_reply_ticket_request import AdminReplyTicketRequest + from .admin_reply_ticket_request_message_type import AdminReplyTicketRequestMessageType + from .admin_reply_ticket_request_reply_options_item import AdminReplyTicketRequestReplyOptionsItem + from .admin_with_app import AdminWithApp + from .admin_with_app_avatar import AdminWithAppAvatar + from .ai_call_response import AiCallResponse + from .app import App + from .article_content import ArticleContent + from .article_content_state import ArticleContentState + from .article_list import ArticleList + from .article_statistics import ArticleStatistics + from .article_translated_content import ArticleTranslatedContent + from .assign_conversation_request import AssignConversationRequest + from .assign_conversation_request_type import AssignConversationRequestType + from .away_status_reason import AwayStatusReason + from .call_list import CallList + from .close_conversation_request import CloseConversationRequest + from .collection_list import CollectionList + from .company_attached_contacts import CompanyAttachedContacts + from .company_attached_segments import CompanyAttachedSegments + from .company_data import CompanyData + from .company_list import CompanyList + from .company_scroll import CompanyScroll + from .contact_archived import ContactArchived + from .contact_attached_companies import ContactAttachedCompanies + from .contact_blocked import ContactBlocked + from .contact_companies import ContactCompanies + from .contact_deleted import ContactDeleted + from .contact_list import ContactList + from .contact_location import ContactLocation + from .contact_notes import ContactNotes + from .contact_reference import ContactReference + from .contact_reply_base_request import ContactReplyBaseRequest + from .contact_reply_base_request_reply_options_item import ContactReplyBaseRequestReplyOptionsItem + from .contact_reply_conversation_request import ContactReplyConversationRequest + from .contact_reply_email_request import ContactReplyEmailRequest + from .contact_reply_intercom_user_id_request import ContactReplyIntercomUserIdRequest + from .contact_reply_ticket_email_request import ContactReplyTicketEmailRequest + from .contact_reply_ticket_intercom_user_id_request import ContactReplyTicketIntercomUserIdRequest + from .contact_reply_ticket_request import ContactReplyTicketRequest + from .contact_reply_ticket_user_id_request import ContactReplyTicketUserIdRequest + from .contact_reply_user_id_request import ContactReplyUserIdRequest + from .contact_segments import ContactSegments + from .contact_social_profiles import ContactSocialProfiles + from .contact_subscription_types import ContactSubscriptionTypes + from .contact_tags import ContactTags + from .contact_unarchived import ContactUnarchived + from .content_sources_list import ContentSourcesList + from .conversation_attachment_files import ConversationAttachmentFiles + from .conversation_attribute_updated_by_admin import ConversationAttributeUpdatedByAdmin + from .conversation_attribute_updated_by_admin_attribute import ConversationAttributeUpdatedByAdminAttribute + from .conversation_attribute_updated_by_admin_value import ConversationAttributeUpdatedByAdminValue + from .conversation_attribute_updated_by_workflow import ConversationAttributeUpdatedByWorkflow + from .conversation_attribute_updated_by_workflow_attribute import ConversationAttributeUpdatedByWorkflowAttribute + from .conversation_attribute_updated_by_workflow_value import ConversationAttributeUpdatedByWorkflowValue + from .conversation_attribute_updated_by_workflow_workflow import ConversationAttributeUpdatedByWorkflowWorkflow + from .conversation_contacts import ConversationContacts + from .conversation_deleted import ConversationDeleted + from .conversation_first_contact_reply import ConversationFirstContactReply + from .conversation_list import ConversationList + from .conversation_part import ConversationPart + from .conversation_part_author import ConversationPartAuthor + from .conversation_part_metadata import ConversationPartMetadata + from .conversation_part_metadata_quick_reply_options_item import ConversationPartMetadataQuickReplyOptionsItem + from .conversation_part_state import ConversationPartState + from .conversation_parts import ConversationParts + from .conversation_rating import ConversationRating + from .conversation_response_time import ConversationResponseTime + from .conversation_source import ConversationSource + from .conversation_source_type import ConversationSourceType + from .conversation_statistics import ConversationStatistics + from .conversation_teammates import ConversationTeammates + from .create_article_request import CreateArticleRequest + from .create_article_request_state import CreateArticleRequestState + from .create_data_attribute_request import CreateDataAttributeRequest + from .create_data_attribute_request_one import CreateDataAttributeRequestOne + from .create_data_attribute_request_one_data_type import CreateDataAttributeRequestOneDataType + from .create_data_attribute_request_options import CreateDataAttributeRequestOptions + from .create_data_attribute_request_options_options_item import CreateDataAttributeRequestOptionsOptionsItem + from .create_internal_article_request import CreateInternalArticleRequest + from .create_message_request import CreateMessageRequest + from .create_or_update_company_request import CreateOrUpdateCompanyRequest + from .create_or_update_tag_request import CreateOrUpdateTagRequest + from .create_phone_switch_request import CreatePhoneSwitchRequest + from .create_ticket_reply_with_comment_request import CreateTicketReplyWithCommentRequest + from .create_ticket_request_assignment import CreateTicketRequestAssignment + from .create_ticket_request_body import CreateTicketRequestBody + from .create_ticket_request_contacts_item import CreateTicketRequestContactsItem + from .create_ticket_request_contacts_item_email import CreateTicketRequestContactsItemEmail + from .create_ticket_request_contacts_item_external_id import CreateTicketRequestContactsItemExternalId + from .create_ticket_request_contacts_item_id import CreateTicketRequestContactsItemId + from .create_ticket_type_request import CreateTicketTypeRequest + from .create_ticket_type_request_category import CreateTicketTypeRequestCategory + from .cursor_pages import CursorPages + from .custom_action_finished import CustomActionFinished + from .custom_action_finished_action import CustomActionFinishedAction + from .custom_action_finished_action_result import CustomActionFinishedActionResult + from .custom_action_started import CustomActionStarted + from .custom_action_started_action import CustomActionStartedAction + from .custom_attributes import CustomAttributes + from .custom_attributes_value import CustomAttributesValue + from .custom_channel_attribute import CustomChannelAttribute + from .custom_channel_base_event import CustomChannelBaseEvent + from .custom_channel_contact import CustomChannelContact + from .custom_channel_contact_type import CustomChannelContactType + from .custom_channel_notification_response import CustomChannelNotificationResponse + from .custom_object_instance_deleted import CustomObjectInstanceDeleted + from .custom_object_instance_list import CustomObjectInstanceList + from .customer_request import CustomerRequest + from .customer_request_email import CustomerRequestEmail + from .customer_request_intercom_user_id import CustomerRequestIntercomUserId + from .customer_request_user_id import CustomerRequestUserId + from .data_attribute_list import DataAttributeList + from .data_event_list import DataEventList + from .data_event_list_pages import DataEventListPages + from .data_event_summary import DataEventSummary + from .data_event_summary_item import DataEventSummaryItem + from .data_export_csv import DataExportCsv + from .datetime import Datetime + from .deleted_article_object import DeletedArticleObject + from .deleted_collection_object import DeletedCollectionObject + from .deleted_company_object import DeletedCompanyObject + from .deleted_internal_article_object import DeletedInternalArticleObject + from .deleted_object import DeletedObject + from .email_address_header import EmailAddressHeader + from .email_message_metadata import EmailMessageMetadata + from .error import Error + from .error_errors_item import ErrorErrorsItem + from .event_details import EventDetails + from .file_attribute import FileAttribute + from .group_content import GroupContent + from .group_translated_content import GroupTranslatedContent + from .internal_article_list import InternalArticleList + from .linked_object import LinkedObject + from .linked_object_category import LinkedObjectCategory + from .linked_object_list import LinkedObjectList + from .linked_object_type import LinkedObjectType + from .multiple_filter_search_request import MultipleFilterSearchRequest + from .multiple_filter_search_request_operator import MultipleFilterSearchRequestOperator + from .multiple_filter_search_request_value import MultipleFilterSearchRequestValue + from .news_item_request import NewsItemRequest + from .news_item_request_state import NewsItemRequestState + from .not_found_error_body import NotFoundErrorBody + from .not_found_error_body_errors_item import NotFoundErrorBodyErrorsItem + from .note_list import NoteList + from .open_conversation_request import OpenConversationRequest + from .operator_workflow_event import OperatorWorkflowEvent + from .operator_workflow_event_event import OperatorWorkflowEventEvent + from .operator_workflow_event_workflow import OperatorWorkflowEventWorkflow + from .pages_link import PagesLink + from .paginated_response import PaginatedResponse + from .paginated_response_data_item import ( + PaginatedResponseDataItem, + PaginatedResponseDataItem_NewsItem, + PaginatedResponseDataItem_Newsfeed, + ) + from .paginated_response_type import PaginatedResponseType + from .part_attachment import PartAttachment + from .phone_switch import PhoneSwitch + from .quick_reply_option import QuickReplyOption + from .recipient import Recipient + from .recipient_type import RecipientType + from .redact_conversation_request import ( + RedactConversationRequest, + RedactConversationRequest_ConversationPart, + RedactConversationRequest_Source, + ) + from .redact_conversation_request_conversation_part import RedactConversationRequestConversationPart + from .redact_conversation_request_source import RedactConversationRequestSource + from .reference import Reference + from .register_fin_voice_call_request import RegisterFinVoiceCallRequest + from .register_fin_voice_call_request_source import RegisterFinVoiceCallRequestSource + from .reply_conversation_request_body import ReplyConversationRequestBody + from .search_request import SearchRequest + from .search_request_query import SearchRequestQuery + from .segment_list import SegmentList + from .single_filter_search_request import SingleFilterSearchRequest + from .single_filter_search_request_operator import SingleFilterSearchRequestOperator + from .single_filter_search_request_value import SingleFilterSearchRequestValue + from .single_filter_search_request_value_two_item import SingleFilterSearchRequestValueTwoItem + from .sla_applied import SlaApplied + from .sla_applied_sla_status import SlaAppliedSlaStatus + from .snooze_conversation_request import SnoozeConversationRequest + from .social_profile import SocialProfile + from .starting_after_paging import StartingAfterPaging + from .subscription_type_list import SubscriptionTypeList + from .tag_company_request import TagCompanyRequest + from .tag_company_request_companies_item import TagCompanyRequestCompaniesItem + from .tag_list import TagList + from .tag_multiple_users_request import TagMultipleUsersRequest + from .tag_multiple_users_request_users_item import TagMultipleUsersRequestUsersItem + from .tags import Tags + from .team_list import TeamList + from .team_priority_level import TeamPriorityLevel + from .ticket_custom_attributes import TicketCustomAttributes + from .ticket_custom_attributes_value import TicketCustomAttributesValue + from .ticket_list import TicketList + from .ticket_part_author import TicketPartAuthor + from .ticket_part_author_type import TicketPartAuthorType + from .ticket_parts import TicketParts + from .ticket_reply import TicketReply + from .ticket_reply_part_type import TicketReplyPartType + from .ticket_request_custom_attributes import TicketRequestCustomAttributes + from .ticket_request_custom_attributes_value import TicketRequestCustomAttributesValue + from .ticket_state_list import TicketStateList + from .ticket_type_attribute import TicketTypeAttribute + from .ticket_type_attribute_list import TicketTypeAttributeList + from .ticket_type_list import TicketTypeList + from .translation import Translation + from .untag_company_request import UntagCompanyRequest + from .untag_company_request_companies_item import UntagCompanyRequestCompaniesItem + from .update_data_attribute_request_body import UpdateDataAttributeRequestBody + from .update_data_attribute_request_options import UpdateDataAttributeRequestOptions + from .update_data_attribute_request_options_options_item import UpdateDataAttributeRequestOptionsOptionsItem + from .visitor import Visitor + from .visitor_avatar import VisitorAvatar + from .visitor_companies import VisitorCompanies + from .visitor_deleted_object import VisitorDeletedObject + from .visitor_location_data import VisitorLocationData + from .visitor_segments import VisitorSegments + from .visitor_social_profiles import VisitorSocialProfiles + from .visitor_tags import VisitorTags + from .visitor_tags_tags_item import VisitorTagsTagsItem + from .whatsapp_message_status_list import WhatsappMessageStatusList + from .whatsapp_message_status_list_events_item import WhatsappMessageStatusListEventsItem + from .whatsapp_message_status_list_events_item_status import WhatsappMessageStatusListEventsItemStatus + from .whatsapp_message_status_list_pages import WhatsappMessageStatusListPages + from .whatsapp_message_status_list_pages_next import WhatsappMessageStatusListPagesNext +_dynamic_imports: typing.Dict[str, str] = { + "ActivityLog": ".activity_log", + "ActivityLogActivityType": ".activity_log_activity_type", + "ActivityLogList": ".activity_log_list", + "ActivityLogMetadata": ".activity_log_metadata", + "ActivityLogPerformedBy": ".activity_log_performed_by", + "AddressableList": ".addressable_list", + "AdminList": ".admin_list", + "AdminPriorityLevel": ".admin_priority_level", + "AdminReplyConversationRequest": ".admin_reply_conversation_request", + "AdminReplyConversationRequestMessageType": ".admin_reply_conversation_request_message_type", + "AdminReplyTicketRequest": ".admin_reply_ticket_request", + "AdminReplyTicketRequestMessageType": ".admin_reply_ticket_request_message_type", + "AdminReplyTicketRequestReplyOptionsItem": ".admin_reply_ticket_request_reply_options_item", + "AdminWithApp": ".admin_with_app", + "AdminWithAppAvatar": ".admin_with_app_avatar", + "AiCallResponse": ".ai_call_response", + "App": ".app", + "ArticleContent": ".article_content", + "ArticleContentState": ".article_content_state", + "ArticleList": ".article_list", + "ArticleStatistics": ".article_statistics", + "ArticleTranslatedContent": ".article_translated_content", + "AssignConversationRequest": ".assign_conversation_request", + "AssignConversationRequestType": ".assign_conversation_request_type", + "AwayStatusReason": ".away_status_reason", + "CallList": ".call_list", + "CloseConversationRequest": ".close_conversation_request", + "CollectionList": ".collection_list", + "CompanyAttachedContacts": ".company_attached_contacts", + "CompanyAttachedSegments": ".company_attached_segments", + "CompanyData": ".company_data", + "CompanyList": ".company_list", + "CompanyScroll": ".company_scroll", + "ContactArchived": ".contact_archived", + "ContactAttachedCompanies": ".contact_attached_companies", + "ContactBlocked": ".contact_blocked", + "ContactCompanies": ".contact_companies", + "ContactDeleted": ".contact_deleted", + "ContactList": ".contact_list", + "ContactLocation": ".contact_location", + "ContactNotes": ".contact_notes", + "ContactReference": ".contact_reference", + "ContactReplyBaseRequest": ".contact_reply_base_request", + "ContactReplyBaseRequestReplyOptionsItem": ".contact_reply_base_request_reply_options_item", + "ContactReplyConversationRequest": ".contact_reply_conversation_request", + "ContactReplyEmailRequest": ".contact_reply_email_request", + "ContactReplyIntercomUserIdRequest": ".contact_reply_intercom_user_id_request", + "ContactReplyTicketEmailRequest": ".contact_reply_ticket_email_request", + "ContactReplyTicketIntercomUserIdRequest": ".contact_reply_ticket_intercom_user_id_request", + "ContactReplyTicketRequest": ".contact_reply_ticket_request", + "ContactReplyTicketUserIdRequest": ".contact_reply_ticket_user_id_request", + "ContactReplyUserIdRequest": ".contact_reply_user_id_request", + "ContactSegments": ".contact_segments", + "ContactSocialProfiles": ".contact_social_profiles", + "ContactSubscriptionTypes": ".contact_subscription_types", + "ContactTags": ".contact_tags", + "ContactUnarchived": ".contact_unarchived", + "ContentSourcesList": ".content_sources_list", + "ConversationAttachmentFiles": ".conversation_attachment_files", + "ConversationAttributeUpdatedByAdmin": ".conversation_attribute_updated_by_admin", + "ConversationAttributeUpdatedByAdminAttribute": ".conversation_attribute_updated_by_admin_attribute", + "ConversationAttributeUpdatedByAdminValue": ".conversation_attribute_updated_by_admin_value", + "ConversationAttributeUpdatedByWorkflow": ".conversation_attribute_updated_by_workflow", + "ConversationAttributeUpdatedByWorkflowAttribute": ".conversation_attribute_updated_by_workflow_attribute", + "ConversationAttributeUpdatedByWorkflowValue": ".conversation_attribute_updated_by_workflow_value", + "ConversationAttributeUpdatedByWorkflowWorkflow": ".conversation_attribute_updated_by_workflow_workflow", + "ConversationContacts": ".conversation_contacts", + "ConversationDeleted": ".conversation_deleted", + "ConversationFirstContactReply": ".conversation_first_contact_reply", + "ConversationList": ".conversation_list", + "ConversationPart": ".conversation_part", + "ConversationPartAuthor": ".conversation_part_author", + "ConversationPartMetadata": ".conversation_part_metadata", + "ConversationPartMetadataQuickReplyOptionsItem": ".conversation_part_metadata_quick_reply_options_item", + "ConversationPartState": ".conversation_part_state", + "ConversationParts": ".conversation_parts", + "ConversationRating": ".conversation_rating", + "ConversationResponseTime": ".conversation_response_time", + "ConversationSource": ".conversation_source", + "ConversationSourceType": ".conversation_source_type", + "ConversationStatistics": ".conversation_statistics", + "ConversationTeammates": ".conversation_teammates", + "CreateArticleRequest": ".create_article_request", + "CreateArticleRequestState": ".create_article_request_state", + "CreateDataAttributeRequest": ".create_data_attribute_request", + "CreateDataAttributeRequestOne": ".create_data_attribute_request_one", + "CreateDataAttributeRequestOneDataType": ".create_data_attribute_request_one_data_type", + "CreateDataAttributeRequestOptions": ".create_data_attribute_request_options", + "CreateDataAttributeRequestOptionsOptionsItem": ".create_data_attribute_request_options_options_item", + "CreateInternalArticleRequest": ".create_internal_article_request", + "CreateMessageRequest": ".create_message_request", + "CreateOrUpdateCompanyRequest": ".create_or_update_company_request", + "CreateOrUpdateTagRequest": ".create_or_update_tag_request", + "CreatePhoneSwitchRequest": ".create_phone_switch_request", + "CreateTicketReplyWithCommentRequest": ".create_ticket_reply_with_comment_request", + "CreateTicketRequestAssignment": ".create_ticket_request_assignment", + "CreateTicketRequestBody": ".create_ticket_request_body", + "CreateTicketRequestContactsItem": ".create_ticket_request_contacts_item", + "CreateTicketRequestContactsItemEmail": ".create_ticket_request_contacts_item_email", + "CreateTicketRequestContactsItemExternalId": ".create_ticket_request_contacts_item_external_id", + "CreateTicketRequestContactsItemId": ".create_ticket_request_contacts_item_id", + "CreateTicketTypeRequest": ".create_ticket_type_request", + "CreateTicketTypeRequestCategory": ".create_ticket_type_request_category", + "CursorPages": ".cursor_pages", + "CustomActionFinished": ".custom_action_finished", + "CustomActionFinishedAction": ".custom_action_finished_action", + "CustomActionFinishedActionResult": ".custom_action_finished_action_result", + "CustomActionStarted": ".custom_action_started", + "CustomActionStartedAction": ".custom_action_started_action", + "CustomAttributes": ".custom_attributes", + "CustomAttributesValue": ".custom_attributes_value", + "CustomChannelAttribute": ".custom_channel_attribute", + "CustomChannelBaseEvent": ".custom_channel_base_event", + "CustomChannelContact": ".custom_channel_contact", + "CustomChannelContactType": ".custom_channel_contact_type", + "CustomChannelNotificationResponse": ".custom_channel_notification_response", + "CustomObjectInstanceDeleted": ".custom_object_instance_deleted", + "CustomObjectInstanceList": ".custom_object_instance_list", + "CustomerRequest": ".customer_request", + "CustomerRequestEmail": ".customer_request_email", + "CustomerRequestIntercomUserId": ".customer_request_intercom_user_id", + "CustomerRequestUserId": ".customer_request_user_id", + "DataAttributeList": ".data_attribute_list", + "DataEventList": ".data_event_list", + "DataEventListPages": ".data_event_list_pages", + "DataEventSummary": ".data_event_summary", + "DataEventSummaryItem": ".data_event_summary_item", + "DataExportCsv": ".data_export_csv", + "Datetime": ".datetime", + "DeletedArticleObject": ".deleted_article_object", + "DeletedCollectionObject": ".deleted_collection_object", + "DeletedCompanyObject": ".deleted_company_object", + "DeletedInternalArticleObject": ".deleted_internal_article_object", + "DeletedObject": ".deleted_object", + "EmailAddressHeader": ".email_address_header", + "EmailMessageMetadata": ".email_message_metadata", + "Error": ".error", + "ErrorErrorsItem": ".error_errors_item", + "EventDetails": ".event_details", + "FileAttribute": ".file_attribute", + "GroupContent": ".group_content", + "GroupTranslatedContent": ".group_translated_content", + "InternalArticleList": ".internal_article_list", + "LinkedObject": ".linked_object", + "LinkedObjectCategory": ".linked_object_category", + "LinkedObjectList": ".linked_object_list", + "LinkedObjectType": ".linked_object_type", + "MultipleFilterSearchRequest": ".multiple_filter_search_request", + "MultipleFilterSearchRequestOperator": ".multiple_filter_search_request_operator", + "MultipleFilterSearchRequestValue": ".multiple_filter_search_request_value", + "NewsItemRequest": ".news_item_request", + "NewsItemRequestState": ".news_item_request_state", + "NotFoundErrorBody": ".not_found_error_body", + "NotFoundErrorBodyErrorsItem": ".not_found_error_body_errors_item", + "NoteList": ".note_list", + "OpenConversationRequest": ".open_conversation_request", + "OperatorWorkflowEvent": ".operator_workflow_event", + "OperatorWorkflowEventEvent": ".operator_workflow_event_event", + "OperatorWorkflowEventWorkflow": ".operator_workflow_event_workflow", + "PagesLink": ".pages_link", + "PaginatedResponse": ".paginated_response", + "PaginatedResponseDataItem": ".paginated_response_data_item", + "PaginatedResponseDataItem_NewsItem": ".paginated_response_data_item", + "PaginatedResponseDataItem_Newsfeed": ".paginated_response_data_item", + "PaginatedResponseType": ".paginated_response_type", + "PartAttachment": ".part_attachment", + "PhoneSwitch": ".phone_switch", + "QuickReplyOption": ".quick_reply_option", + "Recipient": ".recipient", + "RecipientType": ".recipient_type", + "RedactConversationRequest": ".redact_conversation_request", + "RedactConversationRequestConversationPart": ".redact_conversation_request_conversation_part", + "RedactConversationRequestSource": ".redact_conversation_request_source", + "RedactConversationRequest_ConversationPart": ".redact_conversation_request", + "RedactConversationRequest_Source": ".redact_conversation_request", + "Reference": ".reference", + "RegisterFinVoiceCallRequest": ".register_fin_voice_call_request", + "RegisterFinVoiceCallRequestSource": ".register_fin_voice_call_request_source", + "ReplyConversationRequestBody": ".reply_conversation_request_body", + "SearchRequest": ".search_request", + "SearchRequestQuery": ".search_request_query", + "SegmentList": ".segment_list", + "SingleFilterSearchRequest": ".single_filter_search_request", + "SingleFilterSearchRequestOperator": ".single_filter_search_request_operator", + "SingleFilterSearchRequestValue": ".single_filter_search_request_value", + "SingleFilterSearchRequestValueTwoItem": ".single_filter_search_request_value_two_item", + "SlaApplied": ".sla_applied", + "SlaAppliedSlaStatus": ".sla_applied_sla_status", + "SnoozeConversationRequest": ".snooze_conversation_request", + "SocialProfile": ".social_profile", + "StartingAfterPaging": ".starting_after_paging", + "SubscriptionTypeList": ".subscription_type_list", + "TagCompanyRequest": ".tag_company_request", + "TagCompanyRequestCompaniesItem": ".tag_company_request_companies_item", + "TagList": ".tag_list", + "TagMultipleUsersRequest": ".tag_multiple_users_request", + "TagMultipleUsersRequestUsersItem": ".tag_multiple_users_request_users_item", + "Tags": ".tags", + "TeamList": ".team_list", + "TeamPriorityLevel": ".team_priority_level", + "TicketCustomAttributes": ".ticket_custom_attributes", + "TicketCustomAttributesValue": ".ticket_custom_attributes_value", + "TicketList": ".ticket_list", + "TicketPartAuthor": ".ticket_part_author", + "TicketPartAuthorType": ".ticket_part_author_type", + "TicketParts": ".ticket_parts", + "TicketReply": ".ticket_reply", + "TicketReplyPartType": ".ticket_reply_part_type", + "TicketRequestCustomAttributes": ".ticket_request_custom_attributes", + "TicketRequestCustomAttributesValue": ".ticket_request_custom_attributes_value", + "TicketStateList": ".ticket_state_list", + "TicketTypeAttribute": ".ticket_type_attribute", + "TicketTypeAttributeList": ".ticket_type_attribute_list", + "TicketTypeList": ".ticket_type_list", + "Translation": ".translation", + "UntagCompanyRequest": ".untag_company_request", + "UntagCompanyRequestCompaniesItem": ".untag_company_request_companies_item", + "UpdateDataAttributeRequestBody": ".update_data_attribute_request_body", + "UpdateDataAttributeRequestOptions": ".update_data_attribute_request_options", + "UpdateDataAttributeRequestOptionsOptionsItem": ".update_data_attribute_request_options_options_item", + "Visitor": ".visitor", + "VisitorAvatar": ".visitor_avatar", + "VisitorCompanies": ".visitor_companies", + "VisitorDeletedObject": ".visitor_deleted_object", + "VisitorLocationData": ".visitor_location_data", + "VisitorSegments": ".visitor_segments", + "VisitorSocialProfiles": ".visitor_social_profiles", + "VisitorTags": ".visitor_tags", + "VisitorTagsTagsItem": ".visitor_tags_tags_item", + "WhatsappMessageStatusList": ".whatsapp_message_status_list", + "WhatsappMessageStatusListEventsItem": ".whatsapp_message_status_list_events_item", + "WhatsappMessageStatusListEventsItemStatus": ".whatsapp_message_status_list_events_item_status", + "WhatsappMessageStatusListPages": ".whatsapp_message_status_list_pages", + "WhatsappMessageStatusListPagesNext": ".whatsapp_message_status_list_pages_next", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "ActivityLog", + "ActivityLogActivityType", + "ActivityLogList", + "ActivityLogMetadata", + "ActivityLogPerformedBy", + "AddressableList", + "AdminList", + "AdminPriorityLevel", + "AdminReplyConversationRequest", + "AdminReplyConversationRequestMessageType", + "AdminReplyTicketRequest", + "AdminReplyTicketRequestMessageType", + "AdminReplyTicketRequestReplyOptionsItem", + "AdminWithApp", + "AdminWithAppAvatar", + "AiCallResponse", + "App", + "ArticleContent", + "ArticleContentState", + "ArticleList", + "ArticleStatistics", + "ArticleTranslatedContent", + "AssignConversationRequest", + "AssignConversationRequestType", + "AwayStatusReason", + "CallList", + "CloseConversationRequest", + "CollectionList", + "CompanyAttachedContacts", + "CompanyAttachedSegments", + "CompanyData", + "CompanyList", + "CompanyScroll", + "ContactArchived", + "ContactAttachedCompanies", + "ContactBlocked", + "ContactCompanies", + "ContactDeleted", + "ContactList", + "ContactLocation", + "ContactNotes", + "ContactReference", + "ContactReplyBaseRequest", + "ContactReplyBaseRequestReplyOptionsItem", + "ContactReplyConversationRequest", + "ContactReplyEmailRequest", + "ContactReplyIntercomUserIdRequest", + "ContactReplyTicketEmailRequest", + "ContactReplyTicketIntercomUserIdRequest", + "ContactReplyTicketRequest", + "ContactReplyTicketUserIdRequest", + "ContactReplyUserIdRequest", + "ContactSegments", + "ContactSocialProfiles", + "ContactSubscriptionTypes", + "ContactTags", + "ContactUnarchived", + "ContentSourcesList", + "ConversationAttachmentFiles", + "ConversationAttributeUpdatedByAdmin", + "ConversationAttributeUpdatedByAdminAttribute", + "ConversationAttributeUpdatedByAdminValue", + "ConversationAttributeUpdatedByWorkflow", + "ConversationAttributeUpdatedByWorkflowAttribute", + "ConversationAttributeUpdatedByWorkflowValue", + "ConversationAttributeUpdatedByWorkflowWorkflow", + "ConversationContacts", + "ConversationDeleted", + "ConversationFirstContactReply", + "ConversationList", + "ConversationPart", + "ConversationPartAuthor", + "ConversationPartMetadata", + "ConversationPartMetadataQuickReplyOptionsItem", + "ConversationPartState", + "ConversationParts", + "ConversationRating", + "ConversationResponseTime", + "ConversationSource", + "ConversationSourceType", + "ConversationStatistics", + "ConversationTeammates", + "CreateArticleRequest", + "CreateArticleRequestState", + "CreateDataAttributeRequest", + "CreateDataAttributeRequestOne", + "CreateDataAttributeRequestOneDataType", + "CreateDataAttributeRequestOptions", + "CreateDataAttributeRequestOptionsOptionsItem", + "CreateInternalArticleRequest", + "CreateMessageRequest", + "CreateOrUpdateCompanyRequest", + "CreateOrUpdateTagRequest", + "CreatePhoneSwitchRequest", + "CreateTicketReplyWithCommentRequest", + "CreateTicketRequestAssignment", + "CreateTicketRequestBody", + "CreateTicketRequestContactsItem", + "CreateTicketRequestContactsItemEmail", + "CreateTicketRequestContactsItemExternalId", + "CreateTicketRequestContactsItemId", + "CreateTicketTypeRequest", + "CreateTicketTypeRequestCategory", + "CursorPages", + "CustomActionFinished", + "CustomActionFinishedAction", + "CustomActionFinishedActionResult", + "CustomActionStarted", + "CustomActionStartedAction", + "CustomAttributes", + "CustomAttributesValue", + "CustomChannelAttribute", + "CustomChannelBaseEvent", + "CustomChannelContact", + "CustomChannelContactType", + "CustomChannelNotificationResponse", + "CustomObjectInstanceDeleted", + "CustomObjectInstanceList", + "CustomerRequest", + "CustomerRequestEmail", + "CustomerRequestIntercomUserId", + "CustomerRequestUserId", + "DataAttributeList", + "DataEventList", + "DataEventListPages", + "DataEventSummary", + "DataEventSummaryItem", + "DataExportCsv", + "Datetime", + "DeletedArticleObject", + "DeletedCollectionObject", + "DeletedCompanyObject", + "DeletedInternalArticleObject", + "DeletedObject", + "EmailAddressHeader", + "EmailMessageMetadata", + "Error", + "ErrorErrorsItem", + "EventDetails", + "FileAttribute", + "GroupContent", + "GroupTranslatedContent", + "InternalArticleList", + "LinkedObject", + "LinkedObjectCategory", + "LinkedObjectList", + "LinkedObjectType", + "MultipleFilterSearchRequest", + "MultipleFilterSearchRequestOperator", + "MultipleFilterSearchRequestValue", + "NewsItemRequest", + "NewsItemRequestState", + "NotFoundErrorBody", + "NotFoundErrorBodyErrorsItem", + "NoteList", + "OpenConversationRequest", + "OperatorWorkflowEvent", + "OperatorWorkflowEventEvent", + "OperatorWorkflowEventWorkflow", + "PagesLink", + "PaginatedResponse", + "PaginatedResponseDataItem", + "PaginatedResponseDataItem_NewsItem", + "PaginatedResponseDataItem_Newsfeed", + "PaginatedResponseType", + "PartAttachment", + "PhoneSwitch", + "QuickReplyOption", + "Recipient", + "RecipientType", + "RedactConversationRequest", + "RedactConversationRequestConversationPart", + "RedactConversationRequestSource", + "RedactConversationRequest_ConversationPart", + "RedactConversationRequest_Source", + "Reference", + "RegisterFinVoiceCallRequest", + "RegisterFinVoiceCallRequestSource", + "ReplyConversationRequestBody", + "SearchRequest", + "SearchRequestQuery", + "SegmentList", + "SingleFilterSearchRequest", + "SingleFilterSearchRequestOperator", + "SingleFilterSearchRequestValue", + "SingleFilterSearchRequestValueTwoItem", + "SlaApplied", + "SlaAppliedSlaStatus", + "SnoozeConversationRequest", + "SocialProfile", + "StartingAfterPaging", + "SubscriptionTypeList", + "TagCompanyRequest", + "TagCompanyRequestCompaniesItem", + "TagList", + "TagMultipleUsersRequest", + "TagMultipleUsersRequestUsersItem", + "Tags", + "TeamList", + "TeamPriorityLevel", + "TicketCustomAttributes", + "TicketCustomAttributesValue", + "TicketList", + "TicketPartAuthor", + "TicketPartAuthorType", + "TicketParts", + "TicketReply", + "TicketReplyPartType", + "TicketRequestCustomAttributes", + "TicketRequestCustomAttributesValue", + "TicketStateList", + "TicketTypeAttribute", + "TicketTypeAttributeList", + "TicketTypeList", + "Translation", + "UntagCompanyRequest", + "UntagCompanyRequestCompaniesItem", + "UpdateDataAttributeRequestBody", + "UpdateDataAttributeRequestOptions", + "UpdateDataAttributeRequestOptionsOptionsItem", + "Visitor", + "VisitorAvatar", + "VisitorCompanies", + "VisitorDeletedObject", + "VisitorLocationData", + "VisitorSegments", + "VisitorSocialProfiles", + "VisitorTags", + "VisitorTagsTagsItem", + "WhatsappMessageStatusList", + "WhatsappMessageStatusListEventsItem", + "WhatsappMessageStatusListEventsItemStatus", + "WhatsappMessageStatusListPages", + "WhatsappMessageStatusListPagesNext", +] diff --git a/src/intercom/unstable/types/activity_log.py b/src/intercom/unstable/types/activity_log.py new file mode 100644 index 00000000..50acb70c --- /dev/null +++ b/src/intercom/unstable/types/activity_log.py @@ -0,0 +1,47 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .activity_log_activity_type import ActivityLogActivityType +from .activity_log_metadata import ActivityLogMetadata +from .activity_log_performed_by import ActivityLogPerformedBy + + +class ActivityLog(UncheckedBaseModel): + """ + Activities performed by Admins. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the activity. + """ + + performed_by: typing.Optional[ActivityLogPerformedBy] = pydantic.Field(default=None) + """ + Details about the Admin involved in the activity. + """ + + metadata: typing.Optional[ActivityLogMetadata] = None + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the activity was created. + """ + + activity_type: typing.Optional[ActivityLogActivityType] = None + activity_description: typing.Optional[str] = pydantic.Field(default=None) + """ + A sentence or two describing the activity. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/activity_log_activity_type.py b/src/intercom/unstable/types/activity_log_activity_type.py new file mode 100644 index 00000000..d0fc7579 --- /dev/null +++ b/src/intercom/unstable/types/activity_log_activity_type.py @@ -0,0 +1,77 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ActivityLogActivityType = typing.Union[ + typing.Literal[ + "admin_conversation_assignment_limit_change", + "admin_ticket_assignment_limit_change", + "admin_away_mode_change", + "admin_deletion", + "admin_deprovisioned", + "admin_impersonation_end", + "admin_impersonation_start", + "admin_invite_change", + "admin_invite_creation", + "admin_invite_deletion", + "admin_login_failure", + "admin_login_success", + "admin_logout", + "admin_password_reset_request", + "admin_password_reset_success", + "admin_permission_change", + "admin_provisioned", + "admin_two_factor_auth_change", + "admin_unauthorized_sign_in_method", + "app_admin_join", + "app_authentication_method_change", + "app_data_deletion", + "app_data_export", + "app_google_sso_domain_change", + "app_identity_verification_change", + "app_name_change", + "app_outbound_address_change", + "app_package_installation", + "app_package_token_regeneration", + "app_package_uninstallation", + "app_team_creation", + "app_team_deletion", + "app_team_membership_modification", + "app_timezone_change", + "app_webhook_creation", + "app_webhook_deletion", + "articles_in_messenger_enabled_change", + "bulk_delete", + "bulk_export", + "campaign_deletion", + "campaign_state_change", + "conversation_part_deletion", + "conversation_topic_change", + "conversation_topic_creation", + "conversation_topic_deletion", + "help_center_settings_change", + "inbound_conversations_change", + "inbox_access_change", + "message_deletion", + "message_state_change", + "messenger_look_and_feel_change", + "messenger_search_required_change", + "messenger_spaces_change", + "office_hours_change", + "role_change", + "role_creation", + "role_deletion", + "ruleset_activation_title_preview", + "ruleset_creation", + "ruleset_deletion", + "search_browse_enabled_change", + "search_browse_required_change", + "seat_change", + "seat_revoke", + "security_settings_change", + "temporary_expectation_change", + "upfront_email_collection_change", + "welcome_message_change", + ], + typing.Any, +] diff --git a/src/intercom/unstable/types/activity_log_list.py b/src/intercom/unstable/types/activity_log_list.py new file mode 100644 index 00000000..80b585d9 --- /dev/null +++ b/src/intercom/unstable/types/activity_log_list.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .activity_log import ActivityLog +from .cursor_pages import CursorPages + + +class ActivityLogList(UncheckedBaseModel): + """ + A paginated list of activity logs. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `activity_log.list`. + """ + + pages: typing.Optional[CursorPages] = None + activity_logs: typing.Optional[typing.List[typing.Optional[ActivityLog]]] = pydantic.Field(default=None) + """ + An array of activity logs + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/activity_log_metadata.py b/src/intercom/unstable/types/activity_log_metadata.py new file mode 100644 index 00000000..17aaae91 --- /dev/null +++ b/src/intercom/unstable/types/activity_log_metadata.py @@ -0,0 +1,77 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class ActivityLogMetadata(UncheckedBaseModel): + """ + Additional data provided about Admin activity. + """ + + sign_in_method: typing.Optional[str] = pydantic.Field(default=None) + """ + The way the admin signed in. + """ + + external_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the contact which is provided by the Client. + """ + + away_mode: typing.Optional[bool] = pydantic.Field(default=None) + """ + The away mode status which is set to true when away and false when returned. + """ + + away_status_reason: typing.Optional[str] = pydantic.Field(default=None) + """ + The reason the Admin is away. + """ + + reassign_conversations: typing.Optional[bool] = pydantic.Field(default=None) + """ + Indicates if conversations should be reassigned while an Admin is away. + """ + + source: typing.Optional[str] = pydantic.Field(default=None) + """ + The action that initiated the status change. + """ + + auto_changed: typing.Optional[str] = pydantic.Field(default=None) + """ + Indicates if the status was changed automatically or manually. + """ + + update_by: typing.Optional[int] = pydantic.Field(default=None) + """ + The ID of the Admin who initiated the activity. + """ + + update_by_name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the Admin who initiated the activity. + """ + + conversation_assignment_limit: typing.Optional[int] = pydantic.Field(default=None) + """ + The conversation assignment limit value for an admin. + """ + + ticket_assignment_limit: typing.Optional[int] = pydantic.Field(default=None) + """ + The ticket assignment limit value for an admin. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/activity_log_performed_by.py b/src/intercom/unstable/types/activity_log_performed_by.py new file mode 100644 index 00000000..03f51fb6 --- /dev/null +++ b/src/intercom/unstable/types/activity_log_performed_by.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class ActivityLogPerformedBy(UncheckedBaseModel): + """ + Details about the Admin involved in the activity. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `admin`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the admin. + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + The email of the admin. + """ + + ip: typing.Optional[str] = pydantic.Field(default=None) + """ + The IP address of the admin. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/addressable_list.py b/src/intercom/unstable/types/addressable_list.py new file mode 100644 index 00000000..09bb518e --- /dev/null +++ b/src/intercom/unstable/types/addressable_list.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class AddressableList(UncheckedBaseModel): + """ + A list used to access other resources from a parent model. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + The addressable object type + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the addressable object + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + Url to get more company resources for this contact + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/admin_list.py b/src/intercom/unstable/types/admin_list.py new file mode 100644 index 00000000..02b12cf7 --- /dev/null +++ b/src/intercom/unstable/types/admin_list.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..admins.types.admin import Admin + + +class AdminList(UncheckedBaseModel): + """ + A list of admins associated with a given workspace. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `admin.list`. + """ + + admins: typing.Optional[typing.List[typing.Optional[Admin]]] = pydantic.Field(default=None) + """ + A list of admins associated with a given workspace. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/admin_priority_level.py b/src/intercom/unstable/types/admin_priority_level.py new file mode 100644 index 00000000..1b4d2e2b --- /dev/null +++ b/src/intercom/unstable/types/admin_priority_level.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class AdminPriorityLevel(UncheckedBaseModel): + """ + Admin priority levels for the team + """ + + primary_admin_ids: typing.Optional[typing.List[int]] = pydantic.Field(default=None) + """ + The primary admin ids for the team + """ + + secondary_admin_ids: typing.Optional[typing.List[int]] = pydantic.Field(default=None) + """ + The secondary admin ids for the team + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/admin_reply_conversation_request.py b/src/intercom/unstable/types/admin_reply_conversation_request.py new file mode 100644 index 00000000..445e6f5b --- /dev/null +++ b/src/intercom/unstable/types/admin_reply_conversation_request.py @@ -0,0 +1,57 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .admin_reply_conversation_request_message_type import AdminReplyConversationRequestMessageType +from .conversation_attachment_files import ConversationAttachmentFiles +from .quick_reply_option import QuickReplyOption + + +class AdminReplyConversationRequest(UncheckedBaseModel): + """ + Payload of the request to reply on behalf of an admin + """ + + message_type: AdminReplyConversationRequestMessageType + type: typing.Literal["admin"] = "admin" + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The text body of the reply. Notes accept some HTML formatting. Must be present for comment and note message types. + """ + + admin_id: str = pydantic.Field() + """ + The id of the admin who is authoring the comment. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the reply was created. If not provided, the current time will be used. + """ + + reply_options: typing.Optional[typing.List[QuickReplyOption]] = pydantic.Field(default=None) + """ + The quick reply options to display to the end user. Must be present for quick_reply message types. + """ + + attachment_urls: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + A list of image URLs that will be added as attachments. You can include up to 10 URLs. + """ + + attachment_files: typing.Optional[typing.List[ConversationAttachmentFiles]] = pydantic.Field(default=None) + """ + A list of files that will be added as attachments. You can include up to 10 files + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/admin_reply_conversation_request_message_type.py b/src/intercom/unstable/types/admin_reply_conversation_request_message_type.py new file mode 100644 index 00000000..23e821b7 --- /dev/null +++ b/src/intercom/unstable/types/admin_reply_conversation_request_message_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AdminReplyConversationRequestMessageType = typing.Union[typing.Literal["comment", "note", "quick_reply"], typing.Any] diff --git a/src/intercom/unstable/types/admin_reply_ticket_request.py b/src/intercom/unstable/types/admin_reply_ticket_request.py new file mode 100644 index 00000000..2fc3af2b --- /dev/null +++ b/src/intercom/unstable/types/admin_reply_ticket_request.py @@ -0,0 +1,51 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .admin_reply_ticket_request_message_type import AdminReplyTicketRequestMessageType +from .admin_reply_ticket_request_reply_options_item import AdminReplyTicketRequestReplyOptionsItem + + +class AdminReplyTicketRequest(UncheckedBaseModel): + """ + Payload of the request to reply on behalf of an admin + """ + + message_type: AdminReplyTicketRequestMessageType + type: typing.Literal["admin"] = "admin" + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The text body of the reply. Notes accept some HTML formatting. Must be present for comment and note message types. + """ + + admin_id: str = pydantic.Field() + """ + The id of the admin who is authoring the comment. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the reply was created. If not provided, the current time will be used. + """ + + reply_options: typing.Optional[typing.List[AdminReplyTicketRequestReplyOptionsItem]] = pydantic.Field(default=None) + """ + The quick reply options to display. Must be present for quick_reply message types. + """ + + attachment_urls: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + A list of image URLs that will be added as attachments. You can include up to 10 URLs. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/admin_reply_ticket_request_message_type.py b/src/intercom/unstable/types/admin_reply_ticket_request_message_type.py new file mode 100644 index 00000000..1fdcf0c0 --- /dev/null +++ b/src/intercom/unstable/types/admin_reply_ticket_request_message_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AdminReplyTicketRequestMessageType = typing.Union[typing.Literal["comment", "note", "quick_reply"], typing.Any] diff --git a/src/intercom/unstable/types/admin_reply_ticket_request_reply_options_item.py b/src/intercom/unstable/types/admin_reply_ticket_request_reply_options_item.py new file mode 100644 index 00000000..edc8c5f0 --- /dev/null +++ b/src/intercom/unstable/types/admin_reply_ticket_request_reply_options_item.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.serialization import FieldMetadata +from ...core.unchecked_base_model import UncheckedBaseModel + + +class AdminReplyTicketRequestReplyOptionsItem(UncheckedBaseModel): + text: str = pydantic.Field() + """ + The text to display in this quick reply option. + """ + + uuid_: typing_extensions.Annotated[str, FieldMetadata(alias="uuid")] = pydantic.Field() + """ + A unique identifier for this quick reply option. This value will be available within the metadata of the comment ticket part that is created when a user clicks on this reply option. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/admin_with_app.py b/src/intercom/unstable/types/admin_with_app.py new file mode 100644 index 00000000..9af77090 --- /dev/null +++ b/src/intercom/unstable/types/admin_with_app.py @@ -0,0 +1,84 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .admin_with_app_avatar import AdminWithAppAvatar +from .app import App + + +class AdminWithApp(UncheckedBaseModel): + """ + Admins are the teammate accounts that have access to a workspace + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `admin`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the admin. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the admin. + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + The email of the admin. + """ + + job_title: typing.Optional[str] = pydantic.Field(default=None) + """ + The job title of the admin. + """ + + away_mode_enabled: typing.Optional[bool] = pydantic.Field(default=None) + """ + Identifies if this admin is currently set in away mode. + """ + + away_mode_reassign: typing.Optional[bool] = pydantic.Field(default=None) + """ + Identifies if this admin is set to automatically reassign new conversations to the apps default inbox. + """ + + has_inbox_seat: typing.Optional[bool] = pydantic.Field(default=None) + """ + Identifies if this admin has a paid inbox seat to restrict/allow features that require them. + """ + + team_ids: typing.Optional[typing.List[int]] = pydantic.Field(default=None) + """ + This is a list of ids of the teams that this admin is part of. + """ + + avatar: typing.Optional[AdminWithAppAvatar] = pydantic.Field(default=None) + """ + This object represents the avatar associated with the admin. + """ + + email_verified: typing.Optional[bool] = pydantic.Field(default=None) + """ + Identifies if this admin's email is verified. + """ + + app: typing.Optional[App] = pydantic.Field(default=None) + """ + App that the admin belongs to. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/admin_with_app_avatar.py b/src/intercom/unstable/types/admin_with_app_avatar.py new file mode 100644 index 00000000..78b74bf2 --- /dev/null +++ b/src/intercom/unstable/types/admin_with_app_avatar.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class AdminWithAppAvatar(UncheckedBaseModel): + """ + This object represents the avatar associated with the admin. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + This is a string that identifies the type of the object. It will always have the value `avatar`. + """ + + image_url: typing.Optional[str] = pydantic.Field(default=None) + """ + This object represents the avatar associated with the admin. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/ai_call_response.py b/src/intercom/unstable/types/ai_call_response.py new file mode 100644 index 00000000..e9843556 --- /dev/null +++ b/src/intercom/unstable/types/ai_call_response.py @@ -0,0 +1,72 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class AiCallResponse(UncheckedBaseModel): + """ + Response containing information about a Fin Voice call + """ + + id: typing.Optional[int] = pydantic.Field(default=None) + """ + The unique identifier for the external reference + """ + + app_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The workspace identifier + """ + + user_phone_number: typing.Optional[str] = pydantic.Field(default=None) + """ + Phone number in E.164 format for the call + """ + + status: typing.Optional[str] = pydantic.Field(default=None) + """ + Status of the call. Can be "registered", "in-progress", or a resolution state + """ + + intercom_call_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom call identifier, if the call has been matched + """ + + external_call_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The external call identifier from the call provider + """ + + intercom_conversation_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom conversation identifier, if a conversation has been created + """ + + call_transcript: typing.Optional[typing.List[typing.Dict[str, typing.Any]]] = pydantic.Field(default=None) + """ + Array of transcript entries for the call + """ + + call_summary: typing.Optional[str] = pydantic.Field(default=None) + """ + Summary of the call conversation, truncated to 256 characters. Empty string if no summary available. + """ + + intent: typing.Optional[typing.List[typing.Dict[str, typing.Any]]] = pydantic.Field(default=None) + """ + Array of intent classifications for the call + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/app.py b/src/intercom/unstable/types/app.py new file mode 100644 index 00000000..5357f690 --- /dev/null +++ b/src/intercom/unstable/types/app.py @@ -0,0 +1,57 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class App(UncheckedBaseModel): + """ + App is a workspace on Intercom + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + + """ + + id_code: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the app. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the app. + """ + + region: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom region the app is located in. + """ + + timezone: typing.Optional[str] = pydantic.Field(default=None) + """ + The timezone of the region where the app is located. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + When the app was created. + """ + + identity_verification: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether or not the app uses identity verification. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/article_content.py b/src/intercom/unstable/types/article_content.py new file mode 100644 index 00000000..322c386c --- /dev/null +++ b/src/intercom/unstable/types/article_content.py @@ -0,0 +1,68 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .article_content_state import ArticleContentState + + +class ArticleContent(UncheckedBaseModel): + """ + The Content of an Article. + """ + + type: typing.Optional[typing.Literal["article_content"]] = pydantic.Field(default=None) + """ + The type of object - `article_content` . + """ + + title: typing.Optional[str] = pydantic.Field(default=None) + """ + The title of the article. + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + The description of the article. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The body of the article. + """ + + author_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The ID of the author of the article. + """ + + state: typing.Optional[ArticleContentState] = pydantic.Field(default=None) + """ + Whether the article is `published` or is a `draft` . + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the article was created (seconds). + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time when the article was last updated (seconds). + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL of the article. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/article_content_state.py b/src/intercom/unstable/types/article_content_state.py new file mode 100644 index 00000000..8fbede35 --- /dev/null +++ b/src/intercom/unstable/types/article_content_state.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ArticleContentState = typing.Union[typing.Literal["published", "draft"], typing.Any] diff --git a/src/intercom/unstable/types/article_list.py b/src/intercom/unstable/types/article_list.py new file mode 100644 index 00000000..603a0356 --- /dev/null +++ b/src/intercom/unstable/types/article_list.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..articles.types.article_list_item import ArticleListItem +from .cursor_pages import CursorPages + + +class ArticleList(UncheckedBaseModel): + """ + This will return a list of articles for the App. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object - `list`. + """ + + pages: typing.Optional[CursorPages] = None + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + A count of the total number of articles. + """ + + data: typing.Optional[typing.List[ArticleListItem]] = pydantic.Field(default=None) + """ + An array of Article objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/article_statistics.py b/src/intercom/unstable/types/article_statistics.py new file mode 100644 index 00000000..d643c465 --- /dev/null +++ b/src/intercom/unstable/types/article_statistics.py @@ -0,0 +1,57 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class ArticleStatistics(UncheckedBaseModel): + """ + The statistics of an article. + """ + + type: typing.Optional[typing.Literal["article_statistics"]] = pydantic.Field(default=None) + """ + The type of object - `article_statistics`. + """ + + views: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of total views the article has received. + """ + + conversions: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of conversations started from the article. + """ + + reactions: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of total reactions the article has received. + """ + + happy_reaction_percentage: typing.Optional[float] = pydantic.Field(default=None) + """ + The percentage of happy reactions the article has received against other types of reaction. + """ + + neutral_reaction_percentage: typing.Optional[float] = pydantic.Field(default=None) + """ + The percentage of neutral reactions the article has received against other types of reaction. + """ + + sad_reaction_percentage: typing.Optional[float] = pydantic.Field(default=None) + """ + The percentage of sad reactions the article has received against other types of reaction. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/article_translated_content.py b/src/intercom/unstable/types/article_translated_content.py new file mode 100644 index 00000000..20a828aa --- /dev/null +++ b/src/intercom/unstable/types/article_translated_content.py @@ -0,0 +1,221 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.serialization import FieldMetadata +from ...core.unchecked_base_model import UncheckedBaseModel +from .article_content import ArticleContent + + +class ArticleTranslatedContent(UncheckedBaseModel): + """ + The Translated Content of an Article. The keys are the locale codes and the values are the translated content of the article. + """ + + type: typing.Optional[typing.Literal["article_translated_content"]] = pydantic.Field(default=None) + """ + The type of object - article_translated_content. + """ + + ar: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Arabic + """ + + bg: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Bulgarian + """ + + bs: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Bosnian + """ + + ca: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Catalan + """ + + cs: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Czech + """ + + da: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Danish + """ + + de: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in German + """ + + el: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Greek + """ + + en: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in English + """ + + es: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Spanish + """ + + et: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Estonian + """ + + fi: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Finnish + """ + + fr: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in French + """ + + he: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Hebrew + """ + + hr: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Croatian + """ + + hu: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Hungarian + """ + + id: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Indonesian + """ + + it: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Italian + """ + + ja: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Japanese + """ + + ko: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Korean + """ + + lt: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Lithuanian + """ + + lv: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Latvian + """ + + mn: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Mongolian + """ + + nb: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Norwegian + """ + + nl: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Dutch + """ + + pl: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Polish + """ + + pt: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Portuguese (Portugal) + """ + + ro: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Romanian + """ + + ru: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Russian + """ + + sl: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Slovenian + """ + + sr: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Serbian + """ + + sv: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Swedish + """ + + tr: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Turkish + """ + + vi: typing.Optional[ArticleContent] = pydantic.Field(default=None) + """ + The content of the article in Vietnamese + """ + + pt_br: typing_extensions.Annotated[typing.Optional[ArticleContent], FieldMetadata(alias="pt-BR")] = pydantic.Field( + default=None + ) + """ + The content of the article in Portuguese (Brazil) + """ + + zh_cn: typing_extensions.Annotated[typing.Optional[ArticleContent], FieldMetadata(alias="zh-CN")] = pydantic.Field( + default=None + ) + """ + The content of the article in Chinese (China) + """ + + zh_tw: typing_extensions.Annotated[typing.Optional[ArticleContent], FieldMetadata(alias="zh-TW")] = pydantic.Field( + default=None + ) + """ + The content of the article in Chinese (Taiwan) + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/assign_conversation_request.py b/src/intercom/unstable/types/assign_conversation_request.py new file mode 100644 index 00000000..0b5f9379 --- /dev/null +++ b/src/intercom/unstable/types/assign_conversation_request.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .assign_conversation_request_type import AssignConversationRequestType + + +class AssignConversationRequest(UncheckedBaseModel): + """ + Payload of the request to assign a conversation + """ + + type: AssignConversationRequestType + admin_id: str = pydantic.Field() + """ + The id of the admin who is performing the action. + """ + + assignee_id: str = pydantic.Field() + """ + The `id` of the `admin` or `team` which will be assigned the conversation. A conversation can be assigned both an admin and a team.\\nSet `0` if you want this assign to no admin or team (ie. Unassigned). + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + Optionally you can send a response in the conversation when it is assigned. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/assign_conversation_request_type.py b/src/intercom/unstable/types/assign_conversation_request_type.py new file mode 100644 index 00000000..08756b68 --- /dev/null +++ b/src/intercom/unstable/types/assign_conversation_request_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AssignConversationRequestType = typing.Union[typing.Literal["admin", "team"], typing.Any] diff --git a/src/intercom/unstable/types/away_status_reason.py b/src/intercom/unstable/types/away_status_reason.py new file mode 100644 index 00000000..13de24a5 --- /dev/null +++ b/src/intercom/unstable/types/away_status_reason.py @@ -0,0 +1,54 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class AwayStatusReason(UncheckedBaseModel): + type: typing.Optional[str] = None + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the away status reason + """ + + label: typing.Optional[str] = pydantic.Field(default=None) + """ + The display text for the away status reason + """ + + emoji: typing.Optional[str] = pydantic.Field(default=None) + """ + The emoji associated with the status reason + """ + + order: typing.Optional[int] = pydantic.Field(default=None) + """ + The display order of the status reason + """ + + deleted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the status reason has been soft deleted + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The Unix timestamp when the status reason was created + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The Unix timestamp when the status reason was last updated + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/call_list.py b/src/intercom/unstable/types/call_list.py new file mode 100644 index 00000000..9feaa0f1 --- /dev/null +++ b/src/intercom/unstable/types/call_list.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..calls.types.call import Call +from .cursor_pages import CursorPages + + +class CallList(UncheckedBaseModel): + """ + A paginated list of calls. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `list`. + """ + + data: typing.Optional[typing.List[Call]] = pydantic.Field(default=None) + """ + A list of calls. + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + Total number of items available. + """ + + pages: typing.Optional[CursorPages] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/close_conversation_request.py b/src/intercom/unstable/types/close_conversation_request.py new file mode 100644 index 00000000..88b31d03 --- /dev/null +++ b/src/intercom/unstable/types/close_conversation_request.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CloseConversationRequest(UncheckedBaseModel): + """ + Payload of the request to close a conversation + """ + + type: typing.Literal["admin"] = "admin" + admin_id: str = pydantic.Field() + """ + The id of the admin who is performing the action. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + Optionally you can leave a message in the conversation to provide additional context to the user and other teammates. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/collection_list.py b/src/intercom/unstable/types/collection_list.py new file mode 100644 index 00000000..1a381316 --- /dev/null +++ b/src/intercom/unstable/types/collection_list.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..help_center.types.collection import Collection +from .cursor_pages import CursorPages + + +class CollectionList(UncheckedBaseModel): + """ + This will return a list of Collections for the App. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object - `list`. + """ + + pages: typing.Optional[CursorPages] = None + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + A count of the total number of collections. + """ + + data: typing.Optional[typing.List[Collection]] = pydantic.Field(default=None) + """ + An array of collection objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/company_attached_contacts.py b/src/intercom/unstable/types/company_attached_contacts.py new file mode 100644 index 00000000..53ae40f0 --- /dev/null +++ b/src/intercom/unstable/types/company_attached_contacts.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..contacts.types.contact import Contact +from .cursor_pages import CursorPages + + +class CompanyAttachedContacts(UncheckedBaseModel): + """ + A list of Contact Objects + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of object - `list` + """ + + data: typing.Optional[typing.List[Contact]] = pydantic.Field(default=None) + """ + An array containing Contact Objects + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + The total number of contacts + """ + + pages: typing.Optional[CursorPages] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/company_attached_segments.py b/src/intercom/unstable/types/company_attached_segments.py new file mode 100644 index 00000000..c911cdf2 --- /dev/null +++ b/src/intercom/unstable/types/company_attached_segments.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..segments.types.segment import Segment + + +class CompanyAttachedSegments(UncheckedBaseModel): + """ + A list of Segment Objects + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of object - `list` + """ + + data: typing.Optional[typing.List[Segment]] = pydantic.Field(default=None) + """ + An array containing Segment Objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/company_data.py b/src/intercom/unstable/types/company_data.py new file mode 100644 index 00000000..2c61faf9 --- /dev/null +++ b/src/intercom/unstable/types/company_data.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CompanyData(UncheckedBaseModel): + """ + An object containing data about the companies that a contact is associated with. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the company which is given by Intercom. + """ + + type: typing.Optional[typing.Literal["company"]] = pydantic.Field(default=None) + """ + The type of the object. Always company. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The relative URL of the company. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/company_list.py b/src/intercom/unstable/types/company_list.py new file mode 100644 index 00000000..2f6e0f9c --- /dev/null +++ b/src/intercom/unstable/types/company_list.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..companies.types.company import Company +from .cursor_pages import CursorPages + + +class CompanyList(UncheckedBaseModel): + """ + This will return a list of companies for the App. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of object - `list`. + """ + + pages: typing.Optional[CursorPages] = None + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + The total number of companies. + """ + + data: typing.Optional[typing.List[Company]] = pydantic.Field(default=None) + """ + An array containing Company Objects. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/company_scroll.py b/src/intercom/unstable/types/company_scroll.py new file mode 100644 index 00000000..2bf69d71 --- /dev/null +++ b/src/intercom/unstable/types/company_scroll.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..companies.types.company import Company +from .cursor_pages import CursorPages + + +class CompanyScroll(UncheckedBaseModel): + """ + Companies allow you to represent organizations using your product. Each company will have its own description and be associated with contacts. You can fetch, create, update and list companies. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of object - `list` + """ + + data: typing.Optional[typing.List[Company]] = None + pages: typing.Optional[CursorPages] = None + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + The total number of companies + """ + + scroll_param: typing.Optional[str] = pydantic.Field(default=None) + """ + The scroll parameter to use in the next request to fetch the next page of results. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_archived.py b/src/intercom/unstable/types/contact_archived.py new file mode 100644 index 00000000..c4b70015 --- /dev/null +++ b/src/intercom/unstable/types/contact_archived.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact_reference import ContactReference + + +class ContactArchived(ContactReference): + """ + archived contact object + """ + + archived: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the contact is archived or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_attached_companies.py b/src/intercom/unstable/types/contact_attached_companies.py new file mode 100644 index 00000000..9038872b --- /dev/null +++ b/src/intercom/unstable/types/contact_attached_companies.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..companies.types.company import Company +from .pages_link import PagesLink + + +class ContactAttachedCompanies(UncheckedBaseModel): + """ + A list of Company Objects + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of object + """ + + companies: typing.Optional[typing.List[Company]] = pydantic.Field(default=None) + """ + An array containing Company Objects + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + The total number of companies associated to this contact + """ + + pages: typing.Optional[PagesLink] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_blocked.py b/src/intercom/unstable/types/contact_blocked.py new file mode 100644 index 00000000..f5f26072 --- /dev/null +++ b/src/intercom/unstable/types/contact_blocked.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact_reference import ContactReference + + +class ContactBlocked(ContactReference): + """ + blocked contact object + """ + + blocked: typing.Optional[bool] = pydantic.Field(default=None) + """ + Always true. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_companies.py b/src/intercom/unstable/types/contact_companies.py new file mode 100644 index 00000000..7e902829 --- /dev/null +++ b/src/intercom/unstable/types/contact_companies.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .company_data import CompanyData + + +class ContactCompanies(UncheckedBaseModel): + """ + An object with metadata about companies attached to a contact . Up to 10 will be displayed here. Use the url to get more. + """ + + data: typing.Optional[typing.List[CompanyData]] = pydantic.Field(default=None) + """ + An array of company data objects attached to the contact. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + Url to get more company resources for this contact + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + Integer representing the total number of companies attached to this contact + """ + + has_more: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether there's more Addressable Objects to be viewed. If true, use the url to view all + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_deleted.py b/src/intercom/unstable/types/contact_deleted.py new file mode 100644 index 00000000..6c1f065c --- /dev/null +++ b/src/intercom/unstable/types/contact_deleted.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact_reference import ContactReference + + +class ContactDeleted(ContactReference): + """ + deleted contact object + """ + + deleted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the contact is deleted or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_list.py b/src/intercom/unstable/types/contact_list.py new file mode 100644 index 00000000..dbc3a8e2 --- /dev/null +++ b/src/intercom/unstable/types/contact_list.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..contacts.types.contact import Contact +from .cursor_pages import CursorPages + + +class ContactList(UncheckedBaseModel): + """ + Contacts are your users in Intercom. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + Always list + """ + + data: typing.Optional[typing.List[Contact]] = pydantic.Field(default=None) + """ + The list of contact objects + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + A count of the total number of objects. + """ + + pages: typing.Optional[CursorPages] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_location.py b/src/intercom/unstable/types/contact_location.py new file mode 100644 index 00000000..2b1b4c52 --- /dev/null +++ b/src/intercom/unstable/types/contact_location.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class ContactLocation(UncheckedBaseModel): + """ + An object containing location meta data about a Intercom contact. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + Always location + """ + + country: typing.Optional[str] = pydantic.Field(default=None) + """ + The country that the contact is located in + """ + + region: typing.Optional[str] = pydantic.Field(default=None) + """ + The overal region that the contact is located in + """ + + city: typing.Optional[str] = pydantic.Field(default=None) + """ + The city that the contact is located in + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_notes.py b/src/intercom/unstable/types/contact_notes.py new file mode 100644 index 00000000..86998b1f --- /dev/null +++ b/src/intercom/unstable/types/contact_notes.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .addressable_list import AddressableList + + +class ContactNotes(UncheckedBaseModel): + """ + An object containing notes meta data about the notes that a contact has. Up to 10 will be displayed here. Use the url to get more. + """ + + data: typing.Optional[typing.List[AddressableList]] = pydantic.Field(default=None) + """ + This object represents the notes attached to a contact. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + Url to get more company resources for this contact + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + Int representing the total number of companyies attached to this contact + """ + + has_more: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether there's more Addressable Objects to be viewed. If true, use the url to view all + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_reference.py b/src/intercom/unstable/types/contact_reference.py new file mode 100644 index 00000000..f0d97970 --- /dev/null +++ b/src/intercom/unstable/types/contact_reference.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class ContactReference(UncheckedBaseModel): + """ + reference to contact object + """ + + type: typing.Optional[typing.Literal["contact"]] = pydantic.Field(default=None) + """ + always contact + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the contact which is given by Intercom. + """ + + external_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the contact which is provided by the Client. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_reply_base_request.py b/src/intercom/unstable/types/contact_reply_base_request.py new file mode 100644 index 00000000..67d2c30a --- /dev/null +++ b/src/intercom/unstable/types/contact_reply_base_request.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .contact_reply_base_request_reply_options_item import ContactReplyBaseRequestReplyOptionsItem + + +class ContactReplyBaseRequest(UncheckedBaseModel): + message_type: typing.Literal["comment"] = "comment" + type: typing.Literal["user"] = "user" + body: str = pydantic.Field() + """ + The text body of the comment. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the reply was created. If not provided, the current time will be used. + """ + + attachment_urls: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + A list of image URLs that will be added as attachments. You can include up to 10 URLs. + """ + + reply_options: typing.Optional[typing.List[ContactReplyBaseRequestReplyOptionsItem]] = pydantic.Field(default=None) + """ + The quick reply selection the contact wishes to respond with. These map to buttons displayed in the Messenger UI if sent by a bot, or the reply options sent by an Admin via the API. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_reply_base_request_reply_options_item.py b/src/intercom/unstable/types/contact_reply_base_request_reply_options_item.py new file mode 100644 index 00000000..0f16e8a4 --- /dev/null +++ b/src/intercom/unstable/types/contact_reply_base_request_reply_options_item.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.serialization import FieldMetadata +from ...core.unchecked_base_model import UncheckedBaseModel + + +class ContactReplyBaseRequestReplyOptionsItem(UncheckedBaseModel): + text: str = pydantic.Field() + """ + The text of the chosen reply option. + """ + + uuid_: typing_extensions.Annotated[str, FieldMetadata(alias="uuid")] = pydantic.Field() + """ + The unique identifier for the quick reply option selected. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_reply_conversation_request.py b/src/intercom/unstable/types/contact_reply_conversation_request.py new file mode 100644 index 00000000..7803e414 --- /dev/null +++ b/src/intercom/unstable/types/contact_reply_conversation_request.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .contact_reply_email_request import ContactReplyEmailRequest +from .contact_reply_intercom_user_id_request import ContactReplyIntercomUserIdRequest +from .contact_reply_user_id_request import ContactReplyUserIdRequest + +ContactReplyConversationRequest = typing.Union[ + ContactReplyIntercomUserIdRequest, ContactReplyEmailRequest, ContactReplyUserIdRequest +] diff --git a/src/intercom/unstable/types/contact_reply_email_request.py b/src/intercom/unstable/types/contact_reply_email_request.py new file mode 100644 index 00000000..37d8688b --- /dev/null +++ b/src/intercom/unstable/types/contact_reply_email_request.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact_reply_base_request import ContactReplyBaseRequest +from .conversation_attachment_files import ConversationAttachmentFiles + + +class ContactReplyEmailRequest(ContactReplyBaseRequest): + """ + Payload of the request to reply on behalf of a contact using their `email` + """ + + email: str = pydantic.Field() + """ + The email you have defined for the user. + """ + + attachment_files: typing.Optional[typing.List[ConversationAttachmentFiles]] = pydantic.Field(default=None) + """ + A list of files that will be added as attachments. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_reply_intercom_user_id_request.py b/src/intercom/unstable/types/contact_reply_intercom_user_id_request.py new file mode 100644 index 00000000..1c383cc2 --- /dev/null +++ b/src/intercom/unstable/types/contact_reply_intercom_user_id_request.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact_reply_base_request import ContactReplyBaseRequest +from .conversation_attachment_files import ConversationAttachmentFiles + + +class ContactReplyIntercomUserIdRequest(ContactReplyBaseRequest): + """ + Payload of the request to reply on behalf of a contact using their `intercom_user_id` + """ + + intercom_user_id: str = pydantic.Field() + """ + The identifier for the contact as given by Intercom. + """ + + attachment_files: typing.Optional[typing.List[ConversationAttachmentFiles]] = pydantic.Field(default=None) + """ + A list of files that will be added as attachments. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_reply_ticket_email_request.py b/src/intercom/unstable/types/contact_reply_ticket_email_request.py new file mode 100644 index 00000000..96d773c9 --- /dev/null +++ b/src/intercom/unstable/types/contact_reply_ticket_email_request.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact_reply_base_request import ContactReplyBaseRequest + + +class ContactReplyTicketEmailRequest(ContactReplyBaseRequest): + """ + Payload of the request to reply on behalf of a contact using their `email` + """ + + email: str = pydantic.Field() + """ + The email you have defined for the user. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_reply_ticket_intercom_user_id_request.py b/src/intercom/unstable/types/contact_reply_ticket_intercom_user_id_request.py new file mode 100644 index 00000000..daadd83a --- /dev/null +++ b/src/intercom/unstable/types/contact_reply_ticket_intercom_user_id_request.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact_reply_base_request import ContactReplyBaseRequest + + +class ContactReplyTicketIntercomUserIdRequest(ContactReplyBaseRequest): + """ + Payload of the request to reply on behalf of a contact using their `intercom_user_id` + """ + + intercom_user_id: str = pydantic.Field() + """ + The identifier for the contact as given by Intercom. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_reply_ticket_request.py b/src/intercom/unstable/types/contact_reply_ticket_request.py new file mode 100644 index 00000000..34225954 --- /dev/null +++ b/src/intercom/unstable/types/contact_reply_ticket_request.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .contact_reply_ticket_email_request import ContactReplyTicketEmailRequest +from .contact_reply_ticket_intercom_user_id_request import ContactReplyTicketIntercomUserIdRequest +from .contact_reply_ticket_user_id_request import ContactReplyTicketUserIdRequest + +ContactReplyTicketRequest = typing.Union[ + ContactReplyTicketIntercomUserIdRequest, ContactReplyTicketUserIdRequest, ContactReplyTicketEmailRequest +] diff --git a/src/intercom/unstable/types/contact_reply_ticket_user_id_request.py b/src/intercom/unstable/types/contact_reply_ticket_user_id_request.py new file mode 100644 index 00000000..f4258027 --- /dev/null +++ b/src/intercom/unstable/types/contact_reply_ticket_user_id_request.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact_reply_base_request import ContactReplyBaseRequest + + +class ContactReplyTicketUserIdRequest(ContactReplyBaseRequest): + """ + Payload of the request to reply on behalf of a contact using their `user_id` + """ + + user_id: str = pydantic.Field() + """ + The external_id you have defined for the contact. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_reply_user_id_request.py b/src/intercom/unstable/types/contact_reply_user_id_request.py new file mode 100644 index 00000000..0a867a24 --- /dev/null +++ b/src/intercom/unstable/types/contact_reply_user_id_request.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact_reply_base_request import ContactReplyBaseRequest +from .conversation_attachment_files import ConversationAttachmentFiles + + +class ContactReplyUserIdRequest(ContactReplyBaseRequest): + """ + Payload of the request to reply on behalf of a contact using their `user_id` + """ + + user_id: str = pydantic.Field() + """ + The external_id you have defined for the contact. + """ + + attachment_files: typing.Optional[typing.List[ConversationAttachmentFiles]] = pydantic.Field(default=None) + """ + A list of files that will be added as attachments. You can include up to 10 files. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_segments.py b/src/intercom/unstable/types/contact_segments.py new file mode 100644 index 00000000..2cbdf9f3 --- /dev/null +++ b/src/intercom/unstable/types/contact_segments.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..segments.types.segment import Segment + + +class ContactSegments(UncheckedBaseModel): + """ + A list of segments objects attached to a specific contact. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + data: typing.Optional[typing.List[Segment]] = pydantic.Field(default=None) + """ + Segment objects associated with the contact. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_social_profiles.py b/src/intercom/unstable/types/contact_social_profiles.py new file mode 100644 index 00000000..983a77d3 --- /dev/null +++ b/src/intercom/unstable/types/contact_social_profiles.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .social_profile import SocialProfile + + +class ContactSocialProfiles(UncheckedBaseModel): + """ + An object containing social profiles that a contact has. + """ + + data: typing.Optional[typing.List[SocialProfile]] = pydantic.Field(default=None) + """ + A list of social profiles objects associated with the contact. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_subscription_types.py b/src/intercom/unstable/types/contact_subscription_types.py new file mode 100644 index 00000000..91a6dd19 --- /dev/null +++ b/src/intercom/unstable/types/contact_subscription_types.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .addressable_list import AddressableList + + +class ContactSubscriptionTypes(UncheckedBaseModel): + """ + An object containing Subscription Types meta data about the SubscriptionTypes that a contact has. + """ + + data: typing.Optional[typing.List[AddressableList]] = pydantic.Field(default=None) + """ + This object represents the subscriptions attached to a contact. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + Url to get more subscription type resources for this contact + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + Int representing the total number of subscription types attached to this contact + """ + + has_more: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether there's more Addressable Objects to be viewed. If true, use the url to view all + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_tags.py b/src/intercom/unstable/types/contact_tags.py new file mode 100644 index 00000000..ccd5675d --- /dev/null +++ b/src/intercom/unstable/types/contact_tags.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .addressable_list import AddressableList + + +class ContactTags(UncheckedBaseModel): + """ + An object containing tags meta data about the tags that a contact has. Up to 10 will be displayed here. Use the url to get more. + """ + + data: typing.Optional[typing.List[AddressableList]] = pydantic.Field(default=None) + """ + This object represents the tags attached to a contact. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + url to get more tag resources for this contact + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + Int representing the total number of tags attached to this contact + """ + + has_more: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether there's more Addressable Objects to be viewed. If true, use the url to view all + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/contact_unarchived.py b/src/intercom/unstable/types/contact_unarchived.py new file mode 100644 index 00000000..54c245a1 --- /dev/null +++ b/src/intercom/unstable/types/contact_unarchived.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from .contact_reference import ContactReference + + +class ContactUnarchived(ContactReference): + """ + unarchived contact object + """ + + archived: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the contact is archived or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/content_sources_list.py b/src/intercom/unstable/types/content_sources_list.py new file mode 100644 index 00000000..fc41d33d --- /dev/null +++ b/src/intercom/unstable/types/content_sources_list.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..ai_content_source.types.content_source import ContentSource + + +class ContentSourcesList(UncheckedBaseModel): + type: typing.Optional[typing.Literal["content_source.list"]] = None + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + The total number of content sources used by AI Agent in the conversation. + """ + + content_sources: typing.Optional[typing.List[ContentSource]] = pydantic.Field(default=None) + """ + The content sources used by AI Agent in the conversation. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_attachment_files.py b/src/intercom/unstable/types/conversation_attachment_files.py new file mode 100644 index 00000000..12985bb3 --- /dev/null +++ b/src/intercom/unstable/types/conversation_attachment_files.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class ConversationAttachmentFiles(UncheckedBaseModel): + """ + Properties of the attachment files in a conversation part + """ + + content_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The content type of the file + """ + + data: typing.Optional[str] = pydantic.Field(default=None) + """ + The base64 encoded file data. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the file. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_attribute_updated_by_admin.py b/src/intercom/unstable/types/conversation_attribute_updated_by_admin.py new file mode 100644 index 00000000..3d45497b --- /dev/null +++ b/src/intercom/unstable/types/conversation_attribute_updated_by_admin.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .conversation_attribute_updated_by_admin_attribute import ConversationAttributeUpdatedByAdminAttribute +from .conversation_attribute_updated_by_admin_value import ConversationAttributeUpdatedByAdminValue + + +class ConversationAttributeUpdatedByAdmin(UncheckedBaseModel): + """ + Contains details about Custom Data Attributes (CDAs) that were modified by an admin (operator) for conversation part type conversation_attribute_updated_by_admin. + """ + + attribute: typing.Optional[ConversationAttributeUpdatedByAdminAttribute] = None + value: typing.Optional[ConversationAttributeUpdatedByAdminValue] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_attribute_updated_by_admin_attribute.py b/src/intercom/unstable/types/conversation_attribute_updated_by_admin_attribute.py new file mode 100644 index 00000000..9a84534d --- /dev/null +++ b/src/intercom/unstable/types/conversation_attribute_updated_by_admin_attribute.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class ConversationAttributeUpdatedByAdminAttribute(UncheckedBaseModel): + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Name of the CDA updated + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_attribute_updated_by_admin_value.py b/src/intercom/unstable/types/conversation_attribute_updated_by_admin_value.py new file mode 100644 index 00000000..511bc302 --- /dev/null +++ b/src/intercom/unstable/types/conversation_attribute_updated_by_admin_value.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class ConversationAttributeUpdatedByAdminValue(UncheckedBaseModel): + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Value of the CDA updated + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_attribute_updated_by_workflow.py b/src/intercom/unstable/types/conversation_attribute_updated_by_workflow.py new file mode 100644 index 00000000..b13dfad1 --- /dev/null +++ b/src/intercom/unstable/types/conversation_attribute_updated_by_workflow.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .conversation_attribute_updated_by_workflow_attribute import ConversationAttributeUpdatedByWorkflowAttribute +from .conversation_attribute_updated_by_workflow_value import ConversationAttributeUpdatedByWorkflowValue +from .conversation_attribute_updated_by_workflow_workflow import ConversationAttributeUpdatedByWorkflowWorkflow + + +class ConversationAttributeUpdatedByWorkflow(UncheckedBaseModel): + """ + Contains details about the workflow that was triggered and any Custom Data Attributes (CDAs) that were modified during the workflow execution for conversation part type conversation_attribute_updated_by_workflow. + """ + + workflow: typing.Optional[ConversationAttributeUpdatedByWorkflowWorkflow] = None + attribute: typing.Optional[ConversationAttributeUpdatedByWorkflowAttribute] = None + value: typing.Optional[ConversationAttributeUpdatedByWorkflowValue] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_attribute_updated_by_workflow_attribute.py b/src/intercom/unstable/types/conversation_attribute_updated_by_workflow_attribute.py new file mode 100644 index 00000000..e3a25798 --- /dev/null +++ b/src/intercom/unstable/types/conversation_attribute_updated_by_workflow_attribute.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class ConversationAttributeUpdatedByWorkflowAttribute(UncheckedBaseModel): + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Name of the CDA updated + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_attribute_updated_by_workflow_value.py b/src/intercom/unstable/types/conversation_attribute_updated_by_workflow_value.py new file mode 100644 index 00000000..e4655770 --- /dev/null +++ b/src/intercom/unstable/types/conversation_attribute_updated_by_workflow_value.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class ConversationAttributeUpdatedByWorkflowValue(UncheckedBaseModel): + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Value of the CDA updated + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_attribute_updated_by_workflow_workflow.py b/src/intercom/unstable/types/conversation_attribute_updated_by_workflow_workflow.py new file mode 100644 index 00000000..d496c907 --- /dev/null +++ b/src/intercom/unstable/types/conversation_attribute_updated_by_workflow_workflow.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class ConversationAttributeUpdatedByWorkflowWorkflow(UncheckedBaseModel): + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Name of the workflow + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_contacts.py b/src/intercom/unstable/types/conversation_contacts.py new file mode 100644 index 00000000..f1c74b05 --- /dev/null +++ b/src/intercom/unstable/types/conversation_contacts.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .contact_reference import ContactReference + + +class ConversationContacts(UncheckedBaseModel): + """ + The list of contacts (users or leads) involved in this conversation. This will only contain one customer unless more were added via the group conversation feature. + """ + + type: typing.Optional[typing.Literal["contact.list"]] = pydantic.Field(default=None) + """ + + """ + + contacts: typing.Optional[typing.List[ContactReference]] = pydantic.Field(default=None) + """ + The list of contacts (users or leads) involved in this conversation. This will only contain one customer unless more were added via the group conversation feature. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_deleted.py b/src/intercom/unstable/types/conversation_deleted.py new file mode 100644 index 00000000..265b2a75 --- /dev/null +++ b/src/intercom/unstable/types/conversation_deleted.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class ConversationDeleted(UncheckedBaseModel): + """ + deleted conversation object + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the conversation. + """ + + object: typing.Optional[typing.Literal["conversation"]] = pydantic.Field(default=None) + """ + always conversation + """ + + deleted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the conversation is deleted or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_first_contact_reply.py b/src/intercom/unstable/types/conversation_first_contact_reply.py new file mode 100644 index 00000000..30a41a66 --- /dev/null +++ b/src/intercom/unstable/types/conversation_first_contact_reply.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class ConversationFirstContactReply(UncheckedBaseModel): + """ + An object containing information on the first users message. For a contact initiated message this will represent the users original message. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_list.py b/src/intercom/unstable/types/conversation_list.py new file mode 100644 index 00000000..5c5d45f4 --- /dev/null +++ b/src/intercom/unstable/types/conversation_list.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..conversations.types.conversation import Conversation +from .cursor_pages import CursorPages + + +class ConversationList(UncheckedBaseModel): + """ + Conversations are how you can communicate with users in Intercom. They are created when a contact replies to an outbound message, or when one admin directly sends a message to a single contact. + """ + + type: typing.Optional[typing.Literal["conversation.list"]] = pydantic.Field(default=None) + """ + Always conversation.list + """ + + conversations: typing.Optional[typing.List[Conversation]] = pydantic.Field(default=None) + """ + The list of conversation objects + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + A count of the total number of objects. + """ + + pages: typing.Optional[CursorPages] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_part.py b/src/intercom/unstable/types/conversation_part.py new file mode 100644 index 00000000..f1eda18e --- /dev/null +++ b/src/intercom/unstable/types/conversation_part.py @@ -0,0 +1,104 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..tags.types.tag_basic import TagBasic +from .conversation_part_author import ConversationPartAuthor +from .conversation_part_metadata import ConversationPartMetadata +from .conversation_part_state import ConversationPartState +from .email_message_metadata import EmailMessageMetadata +from .event_details import EventDetails +from .part_attachment import PartAttachment +from .reference import Reference + + +class ConversationPart(UncheckedBaseModel): + """ + A Conversation Part represents a message in the conversation. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + Always conversation_part + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the conversation part. + """ + + part_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of conversation part. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The message body, which may contain HTML. For Twitter, this will show a generic message regarding why the body is obscured. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the conversation part was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The last time the conversation part was updated. + """ + + notified_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the user was notified with the conversation part. + """ + + assigned_to: typing.Optional[Reference] = pydantic.Field(default=None) + """ + The id of the admin that was assigned the conversation by this conversation_part (null if there has been no change in assignment.) + """ + + author: typing.Optional[ConversationPartAuthor] = None + attachments: typing.Optional[typing.List[PartAttachment]] = pydantic.Field(default=None) + """ + A list of attachments for the part. + """ + + external_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The external id of the conversation part + """ + + redacted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether or not the conversation part has been redacted. + """ + + email_message_metadata: typing.Optional[EmailMessageMetadata] = None + metadata: typing.Optional[ConversationPartMetadata] = None + state: typing.Optional[ConversationPartState] = pydantic.Field(default=None) + """ + Indicates the current state of conversation when the conversation part was created. + """ + + tags: typing.Optional[typing.List[TagBasic]] = pydantic.Field(default=None) + """ + A list of tags objects associated with the conversation part. + """ + + event_details: typing.Optional[EventDetails] = None + app_package_code: typing.Optional[str] = pydantic.Field(default=None) + """ + The app package code if this part was created via API. null if the part was not created via API. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_part_author.py b/src/intercom/unstable/types/conversation_part_author.py new file mode 100644 index 00000000..185e7b86 --- /dev/null +++ b/src/intercom/unstable/types/conversation_part_author.py @@ -0,0 +1,52 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class ConversationPartAuthor(UncheckedBaseModel): + """ + The object who initiated the conversation, which can be a Contact, Admin or Team. Bots and campaigns send messages on behalf of Admins or Teams. For Twitter, this will be blank. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of the author + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the author + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the author + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + The email of the author + """ + + from_ai_agent: typing.Optional[bool] = pydantic.Field(default=None) + """ + If this conversation part was sent by the AI Agent + """ + + is_ai_answer: typing.Optional[bool] = pydantic.Field(default=None) + """ + If this conversation part body was generated by the AI Agent + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_part_metadata.py b/src/intercom/unstable/types/conversation_part_metadata.py new file mode 100644 index 00000000..0cfa0f3d --- /dev/null +++ b/src/intercom/unstable/types/conversation_part_metadata.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .conversation_part_metadata_quick_reply_options_item import ConversationPartMetadataQuickReplyOptionsItem + + +class ConversationPartMetadata(UncheckedBaseModel): + """ + Metadata for a conversation part + """ + + quick_reply_options: typing.Optional[typing.List[ConversationPartMetadataQuickReplyOptionsItem]] = pydantic.Field( + default=None + ) + """ + The quick reply options sent by the Admin or bot, presented in this conversation part. + """ + + quick_reply_uuid: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the quick reply option that was clicked by the end user. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_part_metadata_quick_reply_options_item.py b/src/intercom/unstable/types/conversation_part_metadata_quick_reply_options_item.py new file mode 100644 index 00000000..9bc07894 --- /dev/null +++ b/src/intercom/unstable/types/conversation_part_metadata_quick_reply_options_item.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from .quick_reply_option import QuickReplyOption + + +class ConversationPartMetadataQuickReplyOptionsItem(QuickReplyOption): + translations: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + The translations for the quick reply option. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_part_state.py b/src/intercom/unstable/types/conversation_part_state.py new file mode 100644 index 00000000..96342fb5 --- /dev/null +++ b/src/intercom/unstable/types/conversation_part_state.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ConversationPartState = typing.Union[typing.Literal["open", "closed", "snoozed"], typing.Any] diff --git a/src/intercom/unstable/types/conversation_parts.py b/src/intercom/unstable/types/conversation_parts.py new file mode 100644 index 00000000..b3b59402 --- /dev/null +++ b/src/intercom/unstable/types/conversation_parts.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .conversation_part import ConversationPart + + +class ConversationParts(UncheckedBaseModel): + """ + A list of Conversation Part objects for each part message in the conversation. This is only returned when Retrieving a Conversation, and ignored when Listing all Conversations. There is a limit of 500 parts. + """ + + type: typing.Optional[typing.Literal["conversation_part.list"]] = pydantic.Field(default=None) + """ + + """ + + conversation_parts: typing.Optional[typing.List[ConversationPart]] = pydantic.Field(default=None) + """ + A list of Conversation Part objects for each part message in the conversation. This is only returned when Retrieving a Conversation, and ignored when Listing all Conversations. There is a limit of 500 parts. + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_rating.py b/src/intercom/unstable/types/conversation_rating.py new file mode 100644 index 00000000..807baf22 --- /dev/null +++ b/src/intercom/unstable/types/conversation_rating.py @@ -0,0 +1,47 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .contact_reference import ContactReference +from .reference import Reference + + +class ConversationRating(UncheckedBaseModel): + """ + The Conversation Rating object which contains information on the rating and/or remark added by a Contact and the Admin assigned to the conversation. + """ + + rating: typing.Optional[int] = pydantic.Field(default=None) + """ + The rating, between 1 and 5, for the conversation. + """ + + remark: typing.Optional[str] = pydantic.Field(default=None) + """ + An optional field to add a remark to correspond to the number rating + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the rating was requested in the conversation being rated. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the rating was last updated. + """ + + contact: typing.Optional[ContactReference] = None + teammate: typing.Optional[Reference] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_response_time.py b/src/intercom/unstable/types/conversation_response_time.py new file mode 100644 index 00000000..e739209d --- /dev/null +++ b/src/intercom/unstable/types/conversation_response_time.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class ConversationResponseTime(UncheckedBaseModel): + """ + Details of first response time of assigned team in seconds. + """ + + team_id: typing.Optional[int] = pydantic.Field(default=None) + """ + Id of the assigned team. + """ + + team_name: typing.Optional[str] = pydantic.Field(default=None) + """ + Name of the assigned Team, null if team does not exist, Unassigned if no team is assigned. + """ + + response_time: typing.Optional[int] = pydantic.Field(default=None) + """ + First response time of assigned team in seconds. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_source.py b/src/intercom/unstable/types/conversation_source.py new file mode 100644 index 00000000..7e79ca42 --- /dev/null +++ b/src/intercom/unstable/types/conversation_source.py @@ -0,0 +1,66 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .conversation_part_author import ConversationPartAuthor +from .conversation_source_type import ConversationSourceType +from .part_attachment import PartAttachment + + +class ConversationSource(UncheckedBaseModel): + """ + The type of the conversation part that started this conversation. Can be Contact, Admin, Campaign, Automated or Operator initiated. + """ + + type: typing.Optional[ConversationSourceType] = pydantic.Field(default=None) + """ + This includes conversation, email, facebook, instagram, phone_call, phone_switch, push, sms, twitter and whatsapp. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the message. + """ + + delivered_as: typing.Optional[str] = pydantic.Field(default=None) + """ + The conversation's initiation type. Possible values are customer_initiated, campaigns_initiated (legacy campaigns), operator_initiated (Custom bot), automated (Series and other outbounds with dynamic audience message) and admin_initiated (fixed audience message, ticket initiated by an admin, group email). + """ + + subject: typing.Optional[str] = pydantic.Field(default=None) + """ + Optional. The message subject. For Twitter, this will show a generic message regarding why the subject is obscured. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The message body, which may contain HTML. For Twitter, this will show a generic message regarding why the body is obscured. + """ + + author: typing.Optional[ConversationPartAuthor] = None + attachments: typing.Optional[typing.List[PartAttachment]] = pydantic.Field(default=None) + """ + A list of attachments for the part. + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL where the conversation was started. For Twitter, Email, and Bots, this will be blank. + """ + + redacted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether or not the source message has been redacted. Only applicable for contact initiated messages. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_source_type.py b/src/intercom/unstable/types/conversation_source_type.py new file mode 100644 index 00000000..0a56fc31 --- /dev/null +++ b/src/intercom/unstable/types/conversation_source_type.py @@ -0,0 +1,19 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ConversationSourceType = typing.Union[ + typing.Literal[ + "conversation", + "email", + "facebook", + "instagram", + "phone_call", + "phone_switch", + "push", + "sms", + "twitter", + "whatsapp", + ], + typing.Any, +] diff --git a/src/intercom/unstable/types/conversation_statistics.py b/src/intercom/unstable/types/conversation_statistics.py new file mode 100644 index 00000000..9406a3a7 --- /dev/null +++ b/src/intercom/unstable/types/conversation_statistics.py @@ -0,0 +1,142 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .conversation_response_time import ConversationResponseTime + + +class ConversationStatistics(UncheckedBaseModel): + """ + A Statistics object containing all information required for reporting, with timestamps and calculated metrics. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + + """ + + time_to_assignment: typing.Optional[int] = pydantic.Field(default=None) + """ + Duration until last assignment before first admin reply. In seconds. + """ + + time_to_admin_reply: typing.Optional[int] = pydantic.Field(default=None) + """ + Duration until first admin reply. Subtracts out of business hours. In seconds. + """ + + time_to_first_close: typing.Optional[int] = pydantic.Field(default=None) + """ + Duration until conversation was closed first time. Subtracts out of business hours. In seconds. + """ + + time_to_last_close: typing.Optional[int] = pydantic.Field(default=None) + """ + Duration until conversation was closed last time. Subtracts out of business hours. In seconds. + """ + + median_time_to_reply: typing.Optional[int] = pydantic.Field(default=None) + """ + Median based on all admin replies after a contact reply. Subtracts out of business hours. In seconds. + """ + + first_contact_reply_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Time of first text conversation part from a contact. + """ + + first_assignment_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Time of first assignment after first_contact_reply_at. + """ + + first_admin_reply_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Time of first admin reply after first_contact_reply_at. + """ + + first_close_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Time of first close after first_contact_reply_at. + """ + + last_assignment_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Time of last assignment after first_contact_reply_at. + """ + + last_assignment_admin_reply_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Time of first admin reply since most recent assignment. + """ + + last_contact_reply_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Time of the last conversation part from a contact. + """ + + last_admin_reply_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Time of the last conversation part from an admin. + """ + + last_close_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Time of the last conversation close. + """ + + last_closed_by_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The last admin who closed the conversation. Returns a reference to an Admin object. + """ + + count_reopens: typing.Optional[int] = pydantic.Field(default=None) + """ + Number of reopens after first_contact_reply_at. + """ + + count_assignments: typing.Optional[int] = pydantic.Field(default=None) + """ + Number of assignments after first_contact_reply_at. + """ + + count_conversation_parts: typing.Optional[int] = pydantic.Field(default=None) + """ + Total number of conversation parts. + """ + + assigned_team_first_response_time_by_team: typing.Optional[typing.List[ConversationResponseTime]] = pydantic.Field( + default=None + ) + """ + An array of conversation response time objects + """ + + assigned_team_first_response_time_in_office_hours: typing.Optional[typing.List[ConversationResponseTime]] = ( + pydantic.Field(default=None) + ) + """ + An array of conversation response time objects within office hours + """ + + handling_time: typing.Optional[int] = pydantic.Field(default=None) + """ + Time from conversation assignment to conversation close in seconds. + """ + + adjusted_handling_time: typing.Optional[int] = pydantic.Field(default=None) + """ + Adjusted handling time for conversation in seconds. This is the active handling time excluding idle periods when teammates are not actively working on the conversation. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/conversation_teammates.py b/src/intercom/unstable/types/conversation_teammates.py new file mode 100644 index 00000000..64c81313 --- /dev/null +++ b/src/intercom/unstable/types/conversation_teammates.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .reference import Reference + + +class ConversationTeammates(UncheckedBaseModel): + """ + The list of teammates who participated in the conversation (wrote at least one conversation part). + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of the object - `admin.list`. + """ + + teammates: typing.Optional[typing.List[Reference]] = pydantic.Field(default=None) + """ + The list of teammates who participated in the conversation (wrote at least one conversation part). + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/create_article_request.py b/src/intercom/unstable/types/create_article_request.py new file mode 100644 index 00000000..067205b3 --- /dev/null +++ b/src/intercom/unstable/types/create_article_request.py @@ -0,0 +1,61 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .article_translated_content import ArticleTranslatedContent +from .create_article_request_state import CreateArticleRequestState + + +class CreateArticleRequest(UncheckedBaseModel): + """ + You can create an Article + """ + + title: str = pydantic.Field() + """ + The title of the article.For multilingual articles, this will be the title of the default language's content. + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + The description of the article. For multilingual articles, this will be the description of the default language's content. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The content of the article. For multilingual articles, this will be the body of the default language's content. + """ + + author_id: int = pydantic.Field() + """ + The id of the author of the article. For multilingual articles, this will be the id of the author of the default language's content. Must be a teammate on the help center's workspace. + """ + + state: typing.Optional[CreateArticleRequestState] = pydantic.Field(default=None) + """ + Whether the article will be `published` or will be a `draft`. Defaults to draft. For multilingual articles, this will be the state of the default language's content. + """ + + parent_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of the article's parent collection or section. An article without this field stands alone. + """ + + parent_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of parent, which can either be a `collection` or `section`. + """ + + translated_content: typing.Optional[ArticleTranslatedContent] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/create_article_request_state.py b/src/intercom/unstable/types/create_article_request_state.py new file mode 100644 index 00000000..2b13f578 --- /dev/null +++ b/src/intercom/unstable/types/create_article_request_state.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CreateArticleRequestState = typing.Union[typing.Literal["published", "draft"], typing.Any] diff --git a/src/intercom/unstable/types/create_data_attribute_request.py b/src/intercom/unstable/types/create_data_attribute_request.py new file mode 100644 index 00000000..cd76eee5 --- /dev/null +++ b/src/intercom/unstable/types/create_data_attribute_request.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .create_data_attribute_request_one import CreateDataAttributeRequestOne +from .create_data_attribute_request_options import CreateDataAttributeRequestOptions + +CreateDataAttributeRequest = typing.Union[CreateDataAttributeRequestOptions, CreateDataAttributeRequestOne] diff --git a/src/intercom/unstable/types/create_data_attribute_request_one.py b/src/intercom/unstable/types/create_data_attribute_request_one.py new file mode 100644 index 00000000..9733ced1 --- /dev/null +++ b/src/intercom/unstable/types/create_data_attribute_request_one.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .create_data_attribute_request_one_data_type import CreateDataAttributeRequestOneDataType + + +class CreateDataAttributeRequestOne(UncheckedBaseModel): + data_type: typing.Optional[CreateDataAttributeRequestOneDataType] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/create_data_attribute_request_one_data_type.py b/src/intercom/unstable/types/create_data_attribute_request_one_data_type.py new file mode 100644 index 00000000..0689e73c --- /dev/null +++ b/src/intercom/unstable/types/create_data_attribute_request_one_data_type.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CreateDataAttributeRequestOneDataType = typing.Union[ + typing.Literal["string", "integer", "float", "boolean", "datetime", "date"], typing.Any +] diff --git a/src/intercom/unstable/types/create_data_attribute_request_options.py b/src/intercom/unstable/types/create_data_attribute_request_options.py new file mode 100644 index 00000000..fb2bcf93 --- /dev/null +++ b/src/intercom/unstable/types/create_data_attribute_request_options.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .create_data_attribute_request_options_options_item import CreateDataAttributeRequestOptionsOptionsItem + + +class CreateDataAttributeRequestOptions(UncheckedBaseModel): + data_type: typing.Optional[typing.Literal["options"]] = None + options: typing.List[CreateDataAttributeRequestOptionsOptionsItem] = pydantic.Field() + """ + Array of objects representing the options of the list, with `value` as the key and the option as the value. At least two options are required. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/create_data_attribute_request_options_options_item.py b/src/intercom/unstable/types/create_data_attribute_request_options_options_item.py new file mode 100644 index 00000000..018f7f66 --- /dev/null +++ b/src/intercom/unstable/types/create_data_attribute_request_options_options_item.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CreateDataAttributeRequestOptionsOptionsItem(UncheckedBaseModel): + value: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/create_internal_article_request.py b/src/intercom/unstable/types/create_internal_article_request.py new file mode 100644 index 00000000..c7710e4c --- /dev/null +++ b/src/intercom/unstable/types/create_internal_article_request.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CreateInternalArticleRequest(UncheckedBaseModel): + """ + You can create an Internal Article + """ + + title: str = pydantic.Field() + """ + The title of the article. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The content of the article. + """ + + author_id: int = pydantic.Field() + """ + The id of the author of the article. + """ + + owner_id: int = pydantic.Field() + """ + The id of the owner of the article. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/create_message_request.py b/src/intercom/unstable/types/create_message_request.py new file mode 100644 index 00000000..9343e247 --- /dev/null +++ b/src/intercom/unstable/types/create_message_request.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CreateMessageRequest = typing.Optional[typing.Any] diff --git a/src/intercom/unstable/types/create_or_update_company_request.py b/src/intercom/unstable/types/create_or_update_company_request.py new file mode 100644 index 00000000..b2b278f1 --- /dev/null +++ b/src/intercom/unstable/types/create_or_update_company_request.py @@ -0,0 +1,67 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CreateOrUpdateCompanyRequest(UncheckedBaseModel): + """ + You can create or update a Company + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the Company + """ + + company_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The company id you have defined for the company. Can't be updated + """ + + plan: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the plan you have associated with the company. + """ + + size: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of employees in this company. + """ + + website: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL for this company's website. Please note that the value specified here is not validated. Accepts any string. + """ + + industry: typing.Optional[str] = pydantic.Field(default=None) + """ + The industry that this company operates in. + """ + + custom_attributes: typing.Optional[typing.Dict[str, str]] = pydantic.Field(default=None) + """ + A hash of key/value pairs containing any other data about the company you want Intercom to store. + """ + + remote_created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the company was created by you. + """ + + monthly_spend: typing.Optional[int] = pydantic.Field(default=None) + """ + How much revenue the company generates for your business. Note that this will truncate floats. i.e. it only allow for whole integers, 155.98 will be truncated to 155. Note that this has an upper limit of 2**31-1 or 2147483647.. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/create_or_update_tag_request.py b/src/intercom/unstable/types/create_or_update_tag_request.py new file mode 100644 index 00000000..be51aeaf --- /dev/null +++ b/src/intercom/unstable/types/create_or_update_tag_request.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CreateOrUpdateTagRequest(UncheckedBaseModel): + """ + You can create or update an existing tag. + """ + + name: str = pydantic.Field() + """ + The name of the tag, which will be created if not found, or the new name for the tag if this is an update request. Names are case insensitive. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of tag to updates. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/create_phone_switch_request.py b/src/intercom/unstable/types/create_phone_switch_request.py new file mode 100644 index 00000000..902e5d0f --- /dev/null +++ b/src/intercom/unstable/types/create_phone_switch_request.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .custom_attributes import CustomAttributes + + +class CreatePhoneSwitchRequest(UncheckedBaseModel): + """ + You can create an phone switch + """ + + phone: str = pydantic.Field() + """ + Phone number in E.164 format, that will receive the SMS to continue the conversation in the Messenger. + """ + + custom_attributes: typing.Optional[CustomAttributes] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/create_ticket_reply_with_comment_request.py b/src/intercom/unstable/types/create_ticket_reply_with_comment_request.py new file mode 100644 index 00000000..107d44a0 --- /dev/null +++ b/src/intercom/unstable/types/create_ticket_reply_with_comment_request.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .admin_reply_ticket_request import AdminReplyTicketRequest +from .contact_reply_ticket_request import ContactReplyTicketRequest + +CreateTicketReplyWithCommentRequest = typing.Union[ContactReplyTicketRequest, AdminReplyTicketRequest] diff --git a/src/intercom/unstable/types/create_ticket_request_assignment.py b/src/intercom/unstable/types/create_ticket_request_assignment.py new file mode 100644 index 00000000..fa4de4f2 --- /dev/null +++ b/src/intercom/unstable/types/create_ticket_request_assignment.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CreateTicketRequestAssignment(UncheckedBaseModel): + admin_assignee_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the admin to which the ticket is assigned. If not provided, the ticket will be unassigned. + """ + + team_assignee_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the team to which the ticket is assigned. If not provided, the ticket will be unassigned. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/create_ticket_request_body.py b/src/intercom/unstable/types/create_ticket_request_body.py new file mode 100644 index 00000000..24e77d7f --- /dev/null +++ b/src/intercom/unstable/types/create_ticket_request_body.py @@ -0,0 +1,55 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .create_ticket_request_assignment import CreateTicketRequestAssignment +from .create_ticket_request_contacts_item import CreateTicketRequestContactsItem + + +class CreateTicketRequestBody(UncheckedBaseModel): + """ + You can create a Ticket + """ + + ticket_type_id: str = pydantic.Field() + """ + The ID of the type of ticket you want to create + """ + + contacts: typing.List[CreateTicketRequestContactsItem] = pydantic.Field() + """ + The list of contacts (users or leads) affected by this ticket. Currently only one is allowed + """ + + conversation_to_link_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the conversation you want to link to the ticket. Here are the valid ways of linking two tickets: + - conversation | back-office ticket + - customer tickets | non-shared back-office ticket + - conversation | tracker ticket + - customer ticket | tracker ticket + """ + + company_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the company that the ticket is associated with. The unique identifier for the company which is given by Intercom + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the ticket was created. If not provided, the current time will be used. + """ + + assignment: typing.Optional[CreateTicketRequestAssignment] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/create_ticket_request_contacts_item.py b/src/intercom/unstable/types/create_ticket_request_contacts_item.py new file mode 100644 index 00000000..2d5d66c6 --- /dev/null +++ b/src/intercom/unstable/types/create_ticket_request_contacts_item.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .create_ticket_request_contacts_item_email import CreateTicketRequestContactsItemEmail +from .create_ticket_request_contacts_item_external_id import CreateTicketRequestContactsItemExternalId +from .create_ticket_request_contacts_item_id import CreateTicketRequestContactsItemId + +CreateTicketRequestContactsItem = typing.Union[ + CreateTicketRequestContactsItemId, CreateTicketRequestContactsItemExternalId, CreateTicketRequestContactsItemEmail +] diff --git a/src/intercom/unstable/types/create_ticket_request_contacts_item_email.py b/src/intercom/unstable/types/create_ticket_request_contacts_item_email.py new file mode 100644 index 00000000..39cf4c24 --- /dev/null +++ b/src/intercom/unstable/types/create_ticket_request_contacts_item_email.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CreateTicketRequestContactsItemEmail(UncheckedBaseModel): + email: str = pydantic.Field() + """ + The email you have defined for the contact who is being added as a participant. If a contact with this email does not exist, one will be created. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/create_ticket_request_contacts_item_external_id.py b/src/intercom/unstable/types/create_ticket_request_contacts_item_external_id.py new file mode 100644 index 00000000..857a1ecb --- /dev/null +++ b/src/intercom/unstable/types/create_ticket_request_contacts_item_external_id.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CreateTicketRequestContactsItemExternalId(UncheckedBaseModel): + external_id: str = pydantic.Field() + """ + The external_id you have defined for the contact who is being added as a participant. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/create_ticket_request_contacts_item_id.py b/src/intercom/unstable/types/create_ticket_request_contacts_item_id.py new file mode 100644 index 00000000..adc100bf --- /dev/null +++ b/src/intercom/unstable/types/create_ticket_request_contacts_item_id.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CreateTicketRequestContactsItemId(UncheckedBaseModel): + id: str = pydantic.Field() + """ + The identifier for the contact as given by Intercom. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/create_ticket_type_request.py b/src/intercom/unstable/types/create_ticket_type_request.py new file mode 100644 index 00000000..59105abb --- /dev/null +++ b/src/intercom/unstable/types/create_ticket_type_request.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .create_ticket_type_request_category import CreateTicketTypeRequestCategory + + +class CreateTicketTypeRequest(UncheckedBaseModel): + """ + The request payload for creating a ticket type. + You can copy the `icon` property for your ticket type from [Twemoji Cheatsheet](https://twemoji-cheatsheet.vercel.app/) + """ + + name: str = pydantic.Field() + """ + The name of the ticket type. + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + The description of the ticket type. + """ + + category: typing.Optional[CreateTicketTypeRequestCategory] = pydantic.Field(default=None) + """ + Category of the Ticket Type. + """ + + icon: typing.Optional[str] = pydantic.Field(default=None) + """ + The icon of the ticket type. + """ + + is_internal: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the tickets associated with this ticket type are intended for internal use only or will be shared with customers. This is currently a limited attribute. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/create_ticket_type_request_category.py b/src/intercom/unstable/types/create_ticket_type_request_category.py new file mode 100644 index 00000000..be7783af --- /dev/null +++ b/src/intercom/unstable/types/create_ticket_type_request_category.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CreateTicketTypeRequestCategory = typing.Union[typing.Literal["Customer", "Back-office", "Tracker"], typing.Any] diff --git a/src/intercom/unstable/types/cursor_pages.py b/src/intercom/unstable/types/cursor_pages.py new file mode 100644 index 00000000..cc3f4bdc --- /dev/null +++ b/src/intercom/unstable/types/cursor_pages.py @@ -0,0 +1,45 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .starting_after_paging import StartingAfterPaging + + +class CursorPages(UncheckedBaseModel): + """ + Cursor-based pagination is a technique used in the Intercom API to navigate through large amounts of data. + A "cursor" or pointer is used to keep track of the current position in the result set, allowing the API to return the data in small chunks or "pages" as needed. + """ + + type: typing.Optional[typing.Literal["pages"]] = pydantic.Field(default=None) + """ + the type of object `pages`. + """ + + page: typing.Optional[int] = pydantic.Field(default=None) + """ + The current page + """ + + next: typing.Optional[StartingAfterPaging] = None + per_page: typing.Optional[int] = pydantic.Field(default=None) + """ + Number of results per page + """ + + total_pages: typing.Optional[int] = pydantic.Field(default=None) + """ + Total number of pages + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/custom_action_finished.py b/src/intercom/unstable/types/custom_action_finished.py new file mode 100644 index 00000000..f96b3911 --- /dev/null +++ b/src/intercom/unstable/types/custom_action_finished.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .custom_action_finished_action import CustomActionFinishedAction + + +class CustomActionFinished(UncheckedBaseModel): + """ + Contains details about final status of the completed action for conversation part type custom_action_finished. + """ + + action: typing.Optional[CustomActionFinishedAction] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/custom_action_finished_action.py b/src/intercom/unstable/types/custom_action_finished_action.py new file mode 100644 index 00000000..19082b9d --- /dev/null +++ b/src/intercom/unstable/types/custom_action_finished_action.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .custom_action_finished_action_result import CustomActionFinishedActionResult + + +class CustomActionFinishedAction(UncheckedBaseModel): + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Name of the action + """ + + result: typing.Optional[CustomActionFinishedActionResult] = pydantic.Field(default=None) + """ + Status of the action + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/custom_action_finished_action_result.py b/src/intercom/unstable/types/custom_action_finished_action_result.py new file mode 100644 index 00000000..0f1e35ab --- /dev/null +++ b/src/intercom/unstable/types/custom_action_finished_action_result.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CustomActionFinishedActionResult = typing.Union[typing.Literal["success", "failed"], typing.Any] diff --git a/src/intercom/unstable/types/custom_action_started.py b/src/intercom/unstable/types/custom_action_started.py new file mode 100644 index 00000000..060fc29d --- /dev/null +++ b/src/intercom/unstable/types/custom_action_started.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .custom_action_started_action import CustomActionStartedAction + + +class CustomActionStarted(UncheckedBaseModel): + """ + Contains details about name of the action that was initiated for conversation part type custom_action_started. + """ + + action: typing.Optional[CustomActionStartedAction] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/custom_action_started_action.py b/src/intercom/unstable/types/custom_action_started_action.py new file mode 100644 index 00000000..daada6c9 --- /dev/null +++ b/src/intercom/unstable/types/custom_action_started_action.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CustomActionStartedAction(UncheckedBaseModel): + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Name of the action + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/custom_attributes.py b/src/intercom/unstable/types/custom_attributes.py new file mode 100644 index 00000000..52b1b173 --- /dev/null +++ b/src/intercom/unstable/types/custom_attributes.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .custom_attributes_value import CustomAttributesValue + +CustomAttributes = typing.Dict[str, CustomAttributesValue] diff --git a/src/intercom/unstable/types/custom_attributes_value.py b/src/intercom/unstable/types/custom_attributes_value.py new file mode 100644 index 00000000..93669f70 --- /dev/null +++ b/src/intercom/unstable/types/custom_attributes_value.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .custom_object_instance_list import CustomObjectInstanceList +from .datetime import Datetime + +CustomAttributesValue = typing.Union[str, int, Datetime, CustomObjectInstanceList] diff --git a/src/intercom/unstable/types/custom_channel_attribute.py b/src/intercom/unstable/types/custom_channel_attribute.py new file mode 100644 index 00000000..4d5efd39 --- /dev/null +++ b/src/intercom/unstable/types/custom_channel_attribute.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CustomChannelAttribute(UncheckedBaseModel): + id: str = pydantic.Field() + """ + Identifier for the attribute being collected. + """ + + value: str = pydantic.Field() + """ + Value provided by the user for the attribute. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/custom_channel_base_event.py b/src/intercom/unstable/types/custom_channel_base_event.py new file mode 100644 index 00000000..0a836d45 --- /dev/null +++ b/src/intercom/unstable/types/custom_channel_base_event.py @@ -0,0 +1,31 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .custom_channel_contact import CustomChannelContact + + +class CustomChannelBaseEvent(UncheckedBaseModel): + event_id: str = pydantic.Field() + """ + Unique identifier for the event. + """ + + external_conversation_id: str = pydantic.Field() + """ + Identifier for the conversation in your application. + """ + + contact: CustomChannelContact + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/custom_channel_contact.py b/src/intercom/unstable/types/custom_channel_contact.py new file mode 100644 index 00000000..84fb391e --- /dev/null +++ b/src/intercom/unstable/types/custom_channel_contact.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .custom_channel_contact_type import CustomChannelContactType + + +class CustomChannelContact(UncheckedBaseModel): + type: CustomChannelContactType = pydantic.Field() + """ + Type of contact, must be "user" or "lead". + """ + + external_id: str = pydantic.Field() + """ + External identifier for the contact. Intercom will take care of the mapping of your external_id with our internal ones so you don't have to worry about it. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Name of the contact. Required for user type. + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + Email address of the contact. Required for user type. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/custom_channel_contact_type.py b/src/intercom/unstable/types/custom_channel_contact_type.py new file mode 100644 index 00000000..de33161a --- /dev/null +++ b/src/intercom/unstable/types/custom_channel_contact_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +CustomChannelContactType = typing.Union[typing.Literal["user", "lead"], typing.Any] diff --git a/src/intercom/unstable/types/custom_channel_notification_response.py b/src/intercom/unstable/types/custom_channel_notification_response.py new file mode 100644 index 00000000..4ccfc992 --- /dev/null +++ b/src/intercom/unstable/types/custom_channel_notification_response.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CustomChannelNotificationResponse(UncheckedBaseModel): + external_conversation_id: str = pydantic.Field() + """ + The external conversation ID provided in the notification request + """ + + conversation_id: str = pydantic.Field() + """ + The Intercom conversation ID mapped to the external conversation ID + """ + + external_contact_id: str = pydantic.Field() + """ + The external contact ID provided in the notification request + """ + + contact_id: str = pydantic.Field() + """ + The Intercom contact ID mapped to the external contact ID + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/custom_object_instance_deleted.py b/src/intercom/unstable/types/custom_object_instance_deleted.py new file mode 100644 index 00000000..b43813ef --- /dev/null +++ b/src/intercom/unstable/types/custom_object_instance_deleted.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CustomObjectInstanceDeleted(UncheckedBaseModel): + """ + deleted custom object instance object + """ + + object: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier of the Custom Object type that defines the structure of the Custom Object instance. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom defined id representing the Custom Object instance. + """ + + deleted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the Custom Object instance is deleted or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/custom_object_instance_list.py b/src/intercom/unstable/types/custom_object_instance_list.py new file mode 100644 index 00000000..1bea85e4 --- /dev/null +++ b/src/intercom/unstable/types/custom_object_instance_list.py @@ -0,0 +1,29 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..custom_object_instances.types.custom_object_instance import CustomObjectInstance + + +class CustomObjectInstanceList(UncheckedBaseModel): + """ + The list of associated custom object instances for a given reference attribute on the parent object. + """ + + type: typing.Optional[str] = None + instances: typing.Optional[typing.List[typing.Optional[CustomObjectInstance]]] = pydantic.Field(default=None) + """ + The list of associated custom object instances for a given reference attribute on the parent object. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/customer_request.py b/src/intercom/unstable/types/customer_request.py new file mode 100644 index 00000000..a61840b5 --- /dev/null +++ b/src/intercom/unstable/types/customer_request.py @@ -0,0 +1,9 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .customer_request_email import CustomerRequestEmail +from .customer_request_intercom_user_id import CustomerRequestIntercomUserId +from .customer_request_user_id import CustomerRequestUserId + +CustomerRequest = typing.Union[CustomerRequestIntercomUserId, CustomerRequestUserId, CustomerRequestEmail] diff --git a/src/intercom/unstable/types/customer_request_email.py b/src/intercom/unstable/types/customer_request_email.py new file mode 100644 index 00000000..3ecd6a8e --- /dev/null +++ b/src/intercom/unstable/types/customer_request_email.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CustomerRequestEmail(UncheckedBaseModel): + email: str = pydantic.Field() + """ + The email you have defined for the contact who is being added as a participant. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/customer_request_intercom_user_id.py b/src/intercom/unstable/types/customer_request_intercom_user_id.py new file mode 100644 index 00000000..5eb5082c --- /dev/null +++ b/src/intercom/unstable/types/customer_request_intercom_user_id.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CustomerRequestIntercomUserId(UncheckedBaseModel): + intercom_user_id: str = pydantic.Field() + """ + The identifier for the contact as given by Intercom. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/customer_request_user_id.py b/src/intercom/unstable/types/customer_request_user_id.py new file mode 100644 index 00000000..b4bf8e34 --- /dev/null +++ b/src/intercom/unstable/types/customer_request_user_id.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class CustomerRequestUserId(UncheckedBaseModel): + user_id: str = pydantic.Field() + """ + The external_id you have defined for the contact who is being added as a participant. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/data_attribute_list.py b/src/intercom/unstable/types/data_attribute_list.py new file mode 100644 index 00000000..a0a49bb6 --- /dev/null +++ b/src/intercom/unstable/types/data_attribute_list.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..data_attributes.types.data_attribute import DataAttribute + + +class DataAttributeList(UncheckedBaseModel): + """ + A list of all data attributes belonging to a workspace for contacts, companies or conversations. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + data: typing.Optional[typing.List[DataAttribute]] = pydantic.Field(default=None) + """ + A list of data attributes + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/data_event_list.py b/src/intercom/unstable/types/data_event_list.py new file mode 100644 index 00000000..920dc25c --- /dev/null +++ b/src/intercom/unstable/types/data_event_list.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..data_events.types.data_event import DataEvent +from .data_event_list_pages import DataEventListPages + + +class DataEventList(UncheckedBaseModel): + """ + This will return a list of data events for the App. + """ + + type: typing.Optional[typing.Literal["event.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + events: typing.Optional[typing.List[DataEvent]] = pydantic.Field(default=None) + """ + A list of data events + """ + + pages: typing.Optional[DataEventListPages] = pydantic.Field(default=None) + """ + Pagination + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/data_event_list_pages.py b/src/intercom/unstable/types/data_event_list_pages.py new file mode 100644 index 00000000..43000488 --- /dev/null +++ b/src/intercom/unstable/types/data_event_list_pages.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class DataEventListPages(UncheckedBaseModel): + """ + Pagination + """ + + next: typing.Optional[str] = None + since: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/data_event_summary.py b/src/intercom/unstable/types/data_event_summary.py new file mode 100644 index 00000000..ad9f6fea --- /dev/null +++ b/src/intercom/unstable/types/data_event_summary.py @@ -0,0 +1,48 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .data_event_summary_item import DataEventSummaryItem + + +class DataEventSummary(UncheckedBaseModel): + """ + This will return a summary of data events for the App. + """ + + type: typing.Optional[typing.Literal["event.summary"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + The email address of the user + """ + + intercom_user_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom user ID of the user + """ + + user_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The user ID of the user + """ + + events: typing.Optional[typing.List[typing.Optional[DataEventSummaryItem]]] = pydantic.Field(default=None) + """ + A summary of data events + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/data_event_summary_item.py b/src/intercom/unstable/types/data_event_summary_item.py new file mode 100644 index 00000000..c1ee6db9 --- /dev/null +++ b/src/intercom/unstable/types/data_event_summary_item.py @@ -0,0 +1,47 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class DataEventSummaryItem(UncheckedBaseModel): + """ + This will return a summary of a data event for the App. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the event + """ + + first: typing.Optional[str] = pydantic.Field(default=None) + """ + The first time the event was sent + """ + + last: typing.Optional[str] = pydantic.Field(default=None) + """ + The last time the event was sent + """ + + count: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of times the event was sent + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + The description of the event + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/data_export_csv.py b/src/intercom/unstable/types/data_export_csv.py new file mode 100644 index 00000000..2e6495db --- /dev/null +++ b/src/intercom/unstable/types/data_export_csv.py @@ -0,0 +1,152 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class DataExportCsv(UncheckedBaseModel): + """ + A CSV output file + """ + + user_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The user_id of the user who was sent the message. + """ + + user_external_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The external_user_id of the user who was sent the message + """ + + company_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The company ID of the user in relation to the message that was sent. Will return -1 if no company is present. + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + The users email who was sent the message. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The full name of the user receiving the message + """ + + ruleset_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the message. + """ + + content_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The specific content that was received. In an A/B test each version has its own Content ID. + """ + + content_type: typing.Optional[str] = pydantic.Field(default=None) + """ + Email, Chat, Post etc. + """ + + content_title: typing.Optional[str] = pydantic.Field(default=None) + """ + The title of the content you see in your Intercom workspace. + """ + + ruleset_version_id: typing.Optional[str] = pydantic.Field(default=None) + """ + As you edit content we record new versions. This ID can help you determine which version of a piece of content that was received. + """ + + receipt_id: typing.Optional[str] = pydantic.Field(default=None) + """ + ID for this receipt. Will be included with any related stats in other files to identify this specific delivery of a message. + """ + + received_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Timestamp for when the receipt was recorded. + """ + + series_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the series that this content is part of. Will return -1 if not part of a series. + """ + + series_title: typing.Optional[str] = pydantic.Field(default=None) + """ + The title of the series that this content is part of. + """ + + node_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the series node that this ruleset is associated with. Each block in a series has a corresponding node_id. + """ + + first_reply: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time a user replied to this message if the content was able to receive replies. + """ + + first_completion: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time a user completed this message if the content was able to be completed e.g. Tours, Surveys. + """ + + first_series_completion: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time the series this message was a part of was completed by the user. + """ + + first_series_disengagement: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time the series this message was a part of was disengaged by the user. + """ + + first_series_exit: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time the series this message was a part of was exited by the user. + """ + + first_goal_success: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time the user met this messages associated goal if one exists. + """ + + first_open: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time the user opened this message. + """ + + first_click: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time the series the user clicked on a link within this message. + """ + + first_dismisall: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time the series the user dismissed this message. + """ + + first_unsubscribe: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time the user unsubscribed from this message. + """ + + first_hard_bounce: typing.Optional[int] = pydantic.Field(default=None) + """ + The first time this message hard bounced for this user + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/datetime.py b/src/intercom/unstable/types/datetime.py new file mode 100644 index 00000000..97bfdacc --- /dev/null +++ b/src/intercom/unstable/types/datetime.py @@ -0,0 +1,6 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +Datetime = typing.Union[dt.datetime, int] diff --git a/src/intercom/unstable/types/deleted_article_object.py b/src/intercom/unstable/types/deleted_article_object.py new file mode 100644 index 00000000..37a646f7 --- /dev/null +++ b/src/intercom/unstable/types/deleted_article_object.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class DeletedArticleObject(UncheckedBaseModel): + """ + Response returned when an object is deleted + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the article which you provided in the URL. + """ + + object: typing.Optional[typing.Literal["article"]] = pydantic.Field(default=None) + """ + The type of object which was deleted. - article + """ + + deleted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the article was deleted successfully or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/deleted_collection_object.py b/src/intercom/unstable/types/deleted_collection_object.py new file mode 100644 index 00000000..a2889b07 --- /dev/null +++ b/src/intercom/unstable/types/deleted_collection_object.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class DeletedCollectionObject(UncheckedBaseModel): + """ + Response returned when an object is deleted + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the collection which you provided in the URL. + """ + + object: typing.Optional[typing.Literal["collection"]] = pydantic.Field(default=None) + """ + The type of object which was deleted. - `collection` + """ + + deleted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the collection was deleted successfully or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/deleted_company_object.py b/src/intercom/unstable/types/deleted_company_object.py new file mode 100644 index 00000000..88701bd3 --- /dev/null +++ b/src/intercom/unstable/types/deleted_company_object.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class DeletedCompanyObject(UncheckedBaseModel): + """ + Response returned when an object is deleted + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the company which is given by Intercom. + """ + + object: typing.Optional[typing.Literal["company"]] = pydantic.Field(default=None) + """ + The type of object which was deleted. - `company` + """ + + deleted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the company was deleted successfully or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/deleted_internal_article_object.py b/src/intercom/unstable/types/deleted_internal_article_object.py new file mode 100644 index 00000000..3aeb4c38 --- /dev/null +++ b/src/intercom/unstable/types/deleted_internal_article_object.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class DeletedInternalArticleObject(UncheckedBaseModel): + """ + Response returned when an object is deleted + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the internal article which you provided in the URL. + """ + + object: typing.Optional[typing.Literal["internal_article"]] = pydantic.Field(default=None) + """ + The type of object which was deleted. - internal_article + """ + + deleted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the internal article was deleted successfully or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/deleted_object.py b/src/intercom/unstable/types/deleted_object.py new file mode 100644 index 00000000..8b1c7498 --- /dev/null +++ b/src/intercom/unstable/types/deleted_object.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class DeletedObject(UncheckedBaseModel): + """ + Response returned when an object is deleted + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the news item which you provided in the URL. + """ + + object: typing.Optional[typing.Literal["news-item"]] = pydantic.Field(default=None) + """ + The type of object which was deleted - news-item. + """ + + deleted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the news item was deleted successfully or not. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/email_address_header.py b/src/intercom/unstable/types/email_address_header.py new file mode 100644 index 00000000..7b40706c --- /dev/null +++ b/src/intercom/unstable/types/email_address_header.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class EmailAddressHeader(UncheckedBaseModel): + """ + Contains data for an email address header for a conversation part that was sent as an email. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of email address header + """ + + email_address: typing.Optional[str] = pydantic.Field(default=None) + """ + The email address + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name associated with the email address + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/email_message_metadata.py b/src/intercom/unstable/types/email_message_metadata.py new file mode 100644 index 00000000..3dbcb7c5 --- /dev/null +++ b/src/intercom/unstable/types/email_message_metadata.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .email_address_header import EmailAddressHeader + + +class EmailMessageMetadata(UncheckedBaseModel): + """ + Contains metadata if the message was sent as an email + """ + + subject: typing.Optional[str] = pydantic.Field(default=None) + """ + The subject of the email + """ + + email_address_headers: typing.Optional[typing.List[EmailAddressHeader]] = pydantic.Field(default=None) + """ + A list of an email address headers. + """ + + message_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the email message as specified in the Message-ID header + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/error.py b/src/intercom/unstable/types/error.py new file mode 100644 index 00000000..f60dc286 --- /dev/null +++ b/src/intercom/unstable/types/error.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .error_errors_item import ErrorErrorsItem + + +class Error(UncheckedBaseModel): + """ + The API will return an Error List for a failed request, which will contain one or more Error objects. + """ + + type: str = pydantic.Field() + """ + The type is error.list + """ + + request_id: typing.Optional[str] = pydantic.Field(default=None) + """ + + """ + + errors: typing.List[ErrorErrorsItem] = pydantic.Field() + """ + An array of one or more error objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/error_errors_item.py b/src/intercom/unstable/types/error_errors_item.py new file mode 100644 index 00000000..c0bc619b --- /dev/null +++ b/src/intercom/unstable/types/error_errors_item.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class ErrorErrorsItem(UncheckedBaseModel): + code: str = pydantic.Field() + """ + A string indicating the kind of error, used to further qualify the HTTP response code + """ + + message: typing.Optional[str] = pydantic.Field(default=None) + """ + Optional. Human readable description of the error. + """ + + field: typing.Optional[str] = pydantic.Field(default=None) + """ + Optional. Used to identify a particular field or query parameter that was in error. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/event_details.py b/src/intercom/unstable/types/event_details.py new file mode 100644 index 00000000..2bef2220 --- /dev/null +++ b/src/intercom/unstable/types/event_details.py @@ -0,0 +1,17 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .conversation_attribute_updated_by_admin import ConversationAttributeUpdatedByAdmin +from .conversation_attribute_updated_by_workflow import ConversationAttributeUpdatedByWorkflow +from .custom_action_finished import CustomActionFinished +from .custom_action_started import CustomActionStarted +from .operator_workflow_event import OperatorWorkflowEvent + +EventDetails = typing.Union[ + ConversationAttributeUpdatedByWorkflow, + ConversationAttributeUpdatedByAdmin, + CustomActionStarted, + CustomActionFinished, + OperatorWorkflowEvent, +] diff --git a/src/intercom/unstable/types/file_attribute.py b/src/intercom/unstable/types/file_attribute.py new file mode 100644 index 00000000..0d9f92df --- /dev/null +++ b/src/intercom/unstable/types/file_attribute.py @@ -0,0 +1,53 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class FileAttribute(UncheckedBaseModel): + """ + The value describing a file upload set for a custom attribute + """ + + type: typing.Optional[str] = None + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the file + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The url of the file. This is a temporary URL and will expire after 30 minutes. + """ + + content_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of file + """ + + filesize: typing.Optional[int] = pydantic.Field(default=None) + """ + The size of the file in bytes + """ + + width: typing.Optional[int] = pydantic.Field(default=None) + """ + The width of the file in pixels, if applicable + """ + + height: typing.Optional[int] = pydantic.Field(default=None) + """ + The height of the file in pixels, if applicable + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/group_content.py b/src/intercom/unstable/types/group_content.py new file mode 100644 index 00000000..41b6a6fd --- /dev/null +++ b/src/intercom/unstable/types/group_content.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class GroupContent(UncheckedBaseModel): + """ + The Content of a Group. + """ + + type: typing.Optional[typing.Literal["group_content"]] = pydantic.Field(default=None) + """ + The type of object - `group_content` . + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the collection or section. + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + The description of the collection. Only available for collections. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/group_translated_content.py b/src/intercom/unstable/types/group_translated_content.py new file mode 100644 index 00000000..d7d5dd32 --- /dev/null +++ b/src/intercom/unstable/types/group_translated_content.py @@ -0,0 +1,221 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.serialization import FieldMetadata +from ...core.unchecked_base_model import UncheckedBaseModel +from .group_content import GroupContent + + +class GroupTranslatedContent(UncheckedBaseModel): + """ + The Translated Content of an Group. The keys are the locale codes and the values are the translated content of the Group. + """ + + type: typing.Optional[typing.Literal["group_translated_content"]] = pydantic.Field(default=None) + """ + The type of object - group_translated_content. + """ + + ar: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Arabic + """ + + bg: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Bulgarian + """ + + bs: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Bosnian + """ + + ca: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Catalan + """ + + cs: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Czech + """ + + da: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Danish + """ + + de: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in German + """ + + el: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Greek + """ + + en: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in English + """ + + es: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Spanish + """ + + et: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Estonian + """ + + fi: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Finnish + """ + + fr: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in French + """ + + he: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Hebrew + """ + + hr: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Croatian + """ + + hu: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Hungarian + """ + + id: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Indonesian + """ + + it: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Italian + """ + + ja: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Japanese + """ + + ko: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Korean + """ + + lt: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Lithuanian + """ + + lv: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Latvian + """ + + mn: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Mongolian + """ + + nb: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Norwegian + """ + + nl: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Dutch + """ + + pl: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Polish + """ + + pt: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Portuguese (Portugal) + """ + + ro: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Romanian + """ + + ru: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Russian + """ + + sl: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Slovenian + """ + + sr: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Serbian + """ + + sv: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Swedish + """ + + tr: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Turkish + """ + + vi: typing.Optional[GroupContent] = pydantic.Field(default=None) + """ + The content of the group in Vietnamese + """ + + pt_br: typing_extensions.Annotated[typing.Optional[GroupContent], FieldMetadata(alias="pt-BR")] = pydantic.Field( + default=None + ) + """ + The content of the group in Portuguese (Brazil) + """ + + zh_cn: typing_extensions.Annotated[typing.Optional[GroupContent], FieldMetadata(alias="zh-CN")] = pydantic.Field( + default=None + ) + """ + The content of the group in Chinese (China) + """ + + zh_tw: typing_extensions.Annotated[typing.Optional[GroupContent], FieldMetadata(alias="zh-TW")] = pydantic.Field( + default=None + ) + """ + The content of the group in Chinese (Taiwan) + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/internal_article_list.py b/src/intercom/unstable/types/internal_article_list.py new file mode 100644 index 00000000..930fbf92 --- /dev/null +++ b/src/intercom/unstable/types/internal_article_list.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..internal_articles.types.internal_article_list_item import InternalArticleListItem +from .cursor_pages import CursorPages + + +class InternalArticleList(UncheckedBaseModel): + """ + This will return a list of internal articles for the App. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object - `list`. + """ + + pages: typing.Optional[CursorPages] = None + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + A count of the total number of internal articles. + """ + + data: typing.Optional[typing.List[InternalArticleListItem]] = pydantic.Field(default=None) + """ + An array of Internal Article objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/linked_object.py b/src/intercom/unstable/types/linked_object.py new file mode 100644 index 00000000..19f455a3 --- /dev/null +++ b/src/intercom/unstable/types/linked_object.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .linked_object_category import LinkedObjectCategory +from .linked_object_type import LinkedObjectType + + +class LinkedObject(UncheckedBaseModel): + """ + A linked conversation or ticket. + """ + + type: typing.Optional[LinkedObjectType] = pydantic.Field(default=None) + """ + ticket or conversation + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The ID of the linked object + """ + + category: typing.Optional[LinkedObjectCategory] = pydantic.Field(default=None) + """ + Category of the Linked Ticket Object. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/linked_object_category.py b/src/intercom/unstable/types/linked_object_category.py new file mode 100644 index 00000000..bea12bf0 --- /dev/null +++ b/src/intercom/unstable/types/linked_object_category.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +LinkedObjectCategory = typing.Union[typing.Literal["Customer", "Back-office", "Tracker"], typing.Any] diff --git a/src/intercom/unstable/types/linked_object_list.py b/src/intercom/unstable/types/linked_object_list.py new file mode 100644 index 00000000..6650b8b4 --- /dev/null +++ b/src/intercom/unstable/types/linked_object_list.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .linked_object import LinkedObject + + +class LinkedObjectList(UncheckedBaseModel): + """ + An object containing metadata about linked conversations and linked tickets. Up to 1000 can be returned. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + Always list. + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + The total number of linked objects. + """ + + has_more: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether or not there are more linked objects than returned. + """ + + data: typing.Optional[typing.List[LinkedObject]] = pydantic.Field(default=None) + """ + An array containing the linked conversations and linked tickets. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/linked_object_type.py b/src/intercom/unstable/types/linked_object_type.py new file mode 100644 index 00000000..16749f1f --- /dev/null +++ b/src/intercom/unstable/types/linked_object_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +LinkedObjectType = typing.Union[typing.Literal["ticket", "conversation"], typing.Any] diff --git a/src/intercom/unstable/types/multiple_filter_search_request.py b/src/intercom/unstable/types/multiple_filter_search_request.py new file mode 100644 index 00000000..80c032fd --- /dev/null +++ b/src/intercom/unstable/types/multiple_filter_search_request.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, update_forward_refs +from ...core.unchecked_base_model import UncheckedBaseModel + + +class MultipleFilterSearchRequest(UncheckedBaseModel): + """ + Search using Intercoms Search APIs with more than one filter. + """ + + operator: typing.Optional[MultipleFilterSearchRequestOperator] = pydantic.Field(default=None) + """ + An operator to allow boolean inspection between multiple fields. + """ + + value: typing.Optional["MultipleFilterSearchRequestValue"] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +from .multiple_filter_search_request_value import MultipleFilterSearchRequestValue # noqa: E402, I001 +from .single_filter_search_request_value import SingleFilterSearchRequestValue # noqa: E402, I001 +from .single_filter_search_request_value_two_item import SingleFilterSearchRequestValueTwoItem # noqa: E402, I001 +from .single_filter_search_request import SingleFilterSearchRequest # noqa: E402, I001 +from .single_filter_search_request_operator import SingleFilterSearchRequestOperator # noqa: E402, I001 +from .multiple_filter_search_request_operator import MultipleFilterSearchRequestOperator # noqa: E402, I001 + +update_forward_refs( + MultipleFilterSearchRequest, + MultipleFilterSearchRequestOperator=MultipleFilterSearchRequestOperator, + MultipleFilterSearchRequestValue=MultipleFilterSearchRequestValue, + SingleFilterSearchRequest=SingleFilterSearchRequest, + SingleFilterSearchRequestOperator=SingleFilterSearchRequestOperator, + SingleFilterSearchRequestValue=SingleFilterSearchRequestValue, + SingleFilterSearchRequestValueTwoItem=SingleFilterSearchRequestValueTwoItem, +) diff --git a/src/intercom/unstable/types/multiple_filter_search_request_operator.py b/src/intercom/unstable/types/multiple_filter_search_request_operator.py new file mode 100644 index 00000000..38d46593 --- /dev/null +++ b/src/intercom/unstable/types/multiple_filter_search_request_operator.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +MultipleFilterSearchRequestOperator = typing.Union[typing.Literal["AND", "OR"], typing.Any] diff --git a/src/intercom/unstable/types/multiple_filter_search_request_value.py b/src/intercom/unstable/types/multiple_filter_search_request_value.py new file mode 100644 index 00000000..16a7c594 --- /dev/null +++ b/src/intercom/unstable/types/multiple_filter_search_request_value.py @@ -0,0 +1,13 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +from .single_filter_search_request import SingleFilterSearchRequest + +if typing.TYPE_CHECKING: + from .multiple_filter_search_request import MultipleFilterSearchRequest +MultipleFilterSearchRequestValue = typing.Union[ + typing.List["MultipleFilterSearchRequest"], typing.List[SingleFilterSearchRequest] +] diff --git a/src/intercom/unstable/types/news_item_request.py b/src/intercom/unstable/types/news_item_request.py new file mode 100644 index 00000000..714fa5bf --- /dev/null +++ b/src/intercom/unstable/types/news_item_request.py @@ -0,0 +1,64 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..news.types.newsfeed_assignment import NewsfeedAssignment +from .news_item_request_state import NewsItemRequestState + + +class NewsItemRequest(UncheckedBaseModel): + """ + A News Item is a content type in Intercom enabling you to announce product updates, company news, promotions, events and more with your customers. + """ + + title: str = pydantic.Field() + """ + The title of the news item. + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The news item body, which may contain HTML. + """ + + sender_id: int = pydantic.Field() + """ + The id of the sender of the news item. Must be a teammate on the workspace. + """ + + state: typing.Optional[NewsItemRequestState] = pydantic.Field(default=None) + """ + News items will not be visible to your users in the assigned newsfeeds until they are set live. + """ + + deliver_silently: typing.Optional[bool] = pydantic.Field(default=None) + """ + When set to `true`, the news item will appear in the messenger newsfeed without showing a notification badge. + """ + + labels: typing.Optional[typing.List[str]] = pydantic.Field(default=None) + """ + Label names displayed to users to categorize the news item. + """ + + reactions: typing.Optional[typing.List[typing.Optional[str]]] = pydantic.Field(default=None) + """ + Ordered list of emoji reactions to the news item. When empty, reactions are disabled. + """ + + newsfeed_assignments: typing.Optional[typing.List[NewsfeedAssignment]] = pydantic.Field(default=None) + """ + A list of newsfeed_assignments to assign to the specified newsfeed. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/news_item_request_state.py b/src/intercom/unstable/types/news_item_request_state.py new file mode 100644 index 00000000..0c6a2330 --- /dev/null +++ b/src/intercom/unstable/types/news_item_request_state.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +NewsItemRequestState = typing.Union[typing.Literal["draft", "live"], typing.Any] diff --git a/src/intercom/unstable/types/not_found_error_body.py b/src/intercom/unstable/types/not_found_error_body.py new file mode 100644 index 00000000..da3b22b7 --- /dev/null +++ b/src/intercom/unstable/types/not_found_error_body.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .not_found_error_body_errors_item import NotFoundErrorBodyErrorsItem + + +class NotFoundErrorBody(UncheckedBaseModel): + type: str = pydantic.Field() + """ + The type is error.list + """ + + request_id: typing.Optional[str] = pydantic.Field(default=None) + """ + + """ + + errors: typing.List[NotFoundErrorBodyErrorsItem] = pydantic.Field() + """ + An array of one or more error objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/not_found_error_body_errors_item.py b/src/intercom/unstable/types/not_found_error_body_errors_item.py new file mode 100644 index 00000000..a79982e2 --- /dev/null +++ b/src/intercom/unstable/types/not_found_error_body_errors_item.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class NotFoundErrorBodyErrorsItem(UncheckedBaseModel): + code: str = pydantic.Field() + """ + ticket_not_found + """ + + message: typing.Optional[str] = pydantic.Field(default=None) + """ + Ticket not found + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/note_list.py b/src/intercom/unstable/types/note_list.py new file mode 100644 index 00000000..43396f94 --- /dev/null +++ b/src/intercom/unstable/types/note_list.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..notes.types.note import Note +from .cursor_pages import CursorPages + + +class NoteList(UncheckedBaseModel): + """ + A paginated list of notes associated with a contact or a company. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `list`. + """ + + data: typing.Optional[typing.List[Note]] = pydantic.Field(default=None) + """ + An array of notes. + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + A count of the total number of notes. + """ + + pages: typing.Optional[CursorPages] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/open_conversation_request.py b/src/intercom/unstable/types/open_conversation_request.py new file mode 100644 index 00000000..87fcfb09 --- /dev/null +++ b/src/intercom/unstable/types/open_conversation_request.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class OpenConversationRequest(UncheckedBaseModel): + """ + Payload of the request to open a conversation + """ + + admin_id: str = pydantic.Field() + """ + The id of the admin who is performing the action. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/operator_workflow_event.py b/src/intercom/unstable/types/operator_workflow_event.py new file mode 100644 index 00000000..5eb98f27 --- /dev/null +++ b/src/intercom/unstable/types/operator_workflow_event.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .operator_workflow_event_event import OperatorWorkflowEventEvent +from .operator_workflow_event_workflow import OperatorWorkflowEventWorkflow + + +class OperatorWorkflowEvent(UncheckedBaseModel): + """ + Contains details about name of the workflow for conversation part type operator_workflow_event. + """ + + workflow: typing.Optional[OperatorWorkflowEventWorkflow] = None + event: typing.Optional[OperatorWorkflowEventEvent] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/operator_workflow_event_event.py b/src/intercom/unstable/types/operator_workflow_event_event.py new file mode 100644 index 00000000..7fba320f --- /dev/null +++ b/src/intercom/unstable/types/operator_workflow_event_event.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class OperatorWorkflowEventEvent(UncheckedBaseModel): + type: typing.Optional[str] = pydantic.Field(default=None) + """ + Type of the workflow event initiated + """ + + result: typing.Optional[str] = pydantic.Field(default=None) + """ + Result of the workflow event + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/operator_workflow_event_workflow.py b/src/intercom/unstable/types/operator_workflow_event_workflow.py new file mode 100644 index 00000000..874c0cd8 --- /dev/null +++ b/src/intercom/unstable/types/operator_workflow_event_workflow.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class OperatorWorkflowEventWorkflow(UncheckedBaseModel): + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the workflow + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/pages_link.py b/src/intercom/unstable/types/pages_link.py new file mode 100644 index 00000000..954bde99 --- /dev/null +++ b/src/intercom/unstable/types/pages_link.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class PagesLink(UncheckedBaseModel): + """ + The majority of list resources in the API are paginated to allow clients to traverse data over multiple requests. + + Their responses are likely to contain a pages object that hosts pagination links which a client can use to paginate through the data without having to construct a query. The link relations for the pages field are as follows. + """ + + type: typing.Optional[typing.Literal["pages"]] = None + page: typing.Optional[int] = None + next: typing.Optional[str] = pydantic.Field(default=None) + """ + A link to the next page of results. A response that does not contain a next link does not have further data to fetch. + """ + + per_page: typing.Optional[int] = None + total_pages: typing.Optional[int] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/paginated_response.py b/src/intercom/unstable/types/paginated_response.py new file mode 100644 index 00000000..9c0287c4 --- /dev/null +++ b/src/intercom/unstable/types/paginated_response.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .cursor_pages import CursorPages +from .paginated_response_data_item import PaginatedResponseDataItem +from .paginated_response_type import PaginatedResponseType + + +class PaginatedResponse(UncheckedBaseModel): + """ + Paginated Response + """ + + type: typing.Optional[PaginatedResponseType] = pydantic.Field(default=None) + """ + The type of object + """ + + pages: typing.Optional[CursorPages] = None + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + A count of the total number of objects. + """ + + data: typing.Optional[typing.List[PaginatedResponseDataItem]] = pydantic.Field(default=None) + """ + An array of Objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/paginated_response_data_item.py b/src/intercom/unstable/types/paginated_response_data_item.py new file mode 100644 index 00000000..542c89b4 --- /dev/null +++ b/src/intercom/unstable/types/paginated_response_data_item.py @@ -0,0 +1,61 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel, UnionMetadata +from ..news.types.news_item_state import NewsItemState +from ..news.types.newsfeed_assignment import NewsfeedAssignment + + +class PaginatedResponseDataItem_NewsItem(UncheckedBaseModel): + type: typing.Literal["news-item"] = "news-item" + id: typing.Optional[str] = None + workspace_id: typing.Optional[str] = None + title: typing.Optional[str] = None + body: typing.Optional[str] = None + sender_id: typing.Optional[int] = None + state: typing.Optional[NewsItemState] = None + newsfeed_assignments: typing.Optional[typing.List[NewsfeedAssignment]] = None + labels: typing.Optional[typing.List[typing.Optional[str]]] = None + cover_image_url: typing.Optional[str] = None + reactions: typing.Optional[typing.List[typing.Optional[str]]] = None + deliver_silently: typing.Optional[bool] = None + created_at: typing.Optional[int] = None + updated_at: typing.Optional[int] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class PaginatedResponseDataItem_Newsfeed(UncheckedBaseModel): + type: typing.Literal["newsfeed"] = "newsfeed" + id: typing.Optional[str] = None + name: typing.Optional[str] = None + created_at: typing.Optional[int] = None + updated_at: typing.Optional[int] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +PaginatedResponseDataItem = typing_extensions.Annotated[ + typing.Union[PaginatedResponseDataItem_NewsItem, PaginatedResponseDataItem_Newsfeed], + UnionMetadata(discriminant="type"), +] diff --git a/src/intercom/unstable/types/paginated_response_type.py b/src/intercom/unstable/types/paginated_response_type.py new file mode 100644 index 00000000..04cce509 --- /dev/null +++ b/src/intercom/unstable/types/paginated_response_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +PaginatedResponseType = typing.Union[typing.Literal["list", "conversation.list"], typing.Any] diff --git a/src/intercom/unstable/types/part_attachment.py b/src/intercom/unstable/types/part_attachment.py new file mode 100644 index 00000000..2277f4c1 --- /dev/null +++ b/src/intercom/unstable/types/part_attachment.py @@ -0,0 +1,57 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class PartAttachment(UncheckedBaseModel): + """ + The file attached to a part + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of attachment + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the attachment + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The URL of the attachment + """ + + content_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The content type of the attachment + """ + + filesize: typing.Optional[int] = pydantic.Field(default=None) + """ + The size of the attachment + """ + + width: typing.Optional[int] = pydantic.Field(default=None) + """ + The width of the attachment + """ + + height: typing.Optional[int] = pydantic.Field(default=None) + """ + The height of the attachment + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/phone_switch.py b/src/intercom/unstable/types/phone_switch.py new file mode 100644 index 00000000..26ed3d40 --- /dev/null +++ b/src/intercom/unstable/types/phone_switch.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class PhoneSwitch(UncheckedBaseModel): + """ + Phone Switch Response + """ + + type: typing.Optional[typing.Literal["phone_call_redirect"]] = pydantic.Field(default=None) + """ + + """ + + phone: typing.Optional[str] = pydantic.Field(default=None) + """ + Phone number in E.164 format, that has received the SMS to continue the conversation in the Messenger. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/quick_reply_option.py b/src/intercom/unstable/types/quick_reply_option.py new file mode 100644 index 00000000..bb886ade --- /dev/null +++ b/src/intercom/unstable/types/quick_reply_option.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.serialization import FieldMetadata +from ...core.unchecked_base_model import UncheckedBaseModel + + +class QuickReplyOption(UncheckedBaseModel): + text: str = pydantic.Field() + """ + The text to display in this quick reply option. + """ + + uuid_: typing_extensions.Annotated[str, FieldMetadata(alias="uuid")] = pydantic.Field() + """ + A unique identifier for this quick reply option. This value will be available within the metadata of the comment conversation part that is created when a user clicks on this reply option. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/recipient.py b/src/intercom/unstable/types/recipient.py new file mode 100644 index 00000000..f57ee036 --- /dev/null +++ b/src/intercom/unstable/types/recipient.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .recipient_type import RecipientType + + +class Recipient(UncheckedBaseModel): + """ + A recipient of a message + """ + + type: RecipientType = pydantic.Field() + """ + The role associated to the contact - `user` or `lead`. + """ + + id: str = pydantic.Field() + """ + The identifier for the contact which is given by Intercom. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/recipient_type.py b/src/intercom/unstable/types/recipient_type.py new file mode 100644 index 00000000..f423fd01 --- /dev/null +++ b/src/intercom/unstable/types/recipient_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +RecipientType = typing.Union[typing.Literal["user", "lead"], typing.Any] diff --git a/src/intercom/unstable/types/redact_conversation_request.py b/src/intercom/unstable/types/redact_conversation_request.py new file mode 100644 index 00000000..155d64ed --- /dev/null +++ b/src/intercom/unstable/types/redact_conversation_request.py @@ -0,0 +1,46 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import pydantic +import typing_extensions +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel, UnionMetadata + + +class RedactConversationRequest_ConversationPart(UncheckedBaseModel): + type: typing.Literal["conversation_part"] = "conversation_part" + conversation_id: str + conversation_part_id: str + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +class RedactConversationRequest_Source(UncheckedBaseModel): + type: typing.Literal["source"] = "source" + conversation_id: str + source_id: str + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +RedactConversationRequest = typing_extensions.Annotated[ + typing.Union[RedactConversationRequest_ConversationPart, RedactConversationRequest_Source], + UnionMetadata(discriminant="type"), +] diff --git a/src/intercom/unstable/types/redact_conversation_request_conversation_part.py b/src/intercom/unstable/types/redact_conversation_request_conversation_part.py new file mode 100644 index 00000000..c9323dee --- /dev/null +++ b/src/intercom/unstable/types/redact_conversation_request_conversation_part.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class RedactConversationRequestConversationPart(UncheckedBaseModel): + """ + Payload of the request to redact a conversation part + """ + + conversation_id: str = pydantic.Field() + """ + The id of the conversation. + """ + + conversation_part_id: str = pydantic.Field() + """ + The id of the conversation_part. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/redact_conversation_request_source.py b/src/intercom/unstable/types/redact_conversation_request_source.py new file mode 100644 index 00000000..0c297559 --- /dev/null +++ b/src/intercom/unstable/types/redact_conversation_request_source.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class RedactConversationRequestSource(UncheckedBaseModel): + """ + Payload of the request to redact a conversation source + """ + + conversation_id: str = pydantic.Field() + """ + The id of the conversation. + """ + + source_id: str = pydantic.Field() + """ + The id of the source. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/reference.py b/src/intercom/unstable/types/reference.py new file mode 100644 index 00000000..3718aa84 --- /dev/null +++ b/src/intercom/unstable/types/reference.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class Reference(UncheckedBaseModel): + """ + reference to another object + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/register_fin_voice_call_request.py b/src/intercom/unstable/types/register_fin_voice_call_request.py new file mode 100644 index 00000000..a014c24a --- /dev/null +++ b/src/intercom/unstable/types/register_fin_voice_call_request.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .register_fin_voice_call_request_source import RegisterFinVoiceCallRequestSource + + +class RegisterFinVoiceCallRequest(UncheckedBaseModel): + """ + Register a Fin Voice call with Intercom + """ + + phone_number: str = pydantic.Field() + """ + Phone number in E.164 format for the call + """ + + call_id: str = pydantic.Field() + """ + External call identifier from the call provider + """ + + source: typing.Optional[RegisterFinVoiceCallRequestSource] = pydantic.Field(default=None) + """ + Source of the call. Can be "five9", "zoom_phone", or defaults to "aws_connect" + """ + + data: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + Additional metadata about the call + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/register_fin_voice_call_request_source.py b/src/intercom/unstable/types/register_fin_voice_call_request_source.py new file mode 100644 index 00000000..dbb551a8 --- /dev/null +++ b/src/intercom/unstable/types/register_fin_voice_call_request_source.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +RegisterFinVoiceCallRequestSource = typing.Union[typing.Literal["five9", "zoom_phone", "aws_connect"], typing.Any] diff --git a/src/intercom/unstable/types/reply_conversation_request_body.py b/src/intercom/unstable/types/reply_conversation_request_body.py new file mode 100644 index 00000000..f8070ea5 --- /dev/null +++ b/src/intercom/unstable/types/reply_conversation_request_body.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .admin_reply_conversation_request import AdminReplyConversationRequest +from .contact_reply_conversation_request import ContactReplyConversationRequest + +ReplyConversationRequestBody = typing.Union[ContactReplyConversationRequest, AdminReplyConversationRequest] diff --git a/src/intercom/unstable/types/search_request.py b/src/intercom/unstable/types/search_request.py new file mode 100644 index 00000000..b8b29f27 --- /dev/null +++ b/src/intercom/unstable/types/search_request.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, update_forward_refs +from ...core.unchecked_base_model import UncheckedBaseModel +from .search_request_query import SearchRequestQuery +from .starting_after_paging import StartingAfterPaging + + +class SearchRequest(UncheckedBaseModel): + """ + Search using Intercoms Search APIs. + """ + + query: SearchRequestQuery + pagination: typing.Optional[StartingAfterPaging] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + +update_forward_refs(SearchRequest) diff --git a/src/intercom/unstable/types/search_request_query.py b/src/intercom/unstable/types/search_request_query.py new file mode 100644 index 00000000..c338cbe2 --- /dev/null +++ b/src/intercom/unstable/types/search_request_query.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .multiple_filter_search_request import MultipleFilterSearchRequest +from .single_filter_search_request import SingleFilterSearchRequest + +SearchRequestQuery = typing.Union[SingleFilterSearchRequest, MultipleFilterSearchRequest] diff --git a/src/intercom/unstable/types/segment_list.py b/src/intercom/unstable/types/segment_list.py new file mode 100644 index 00000000..a56004a0 --- /dev/null +++ b/src/intercom/unstable/types/segment_list.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..segments.types.segment import Segment + + +class SegmentList(UncheckedBaseModel): + """ + This will return a list of Segment Objects. The result may also have a pages object if the response is paginated. + """ + + type: typing.Optional[typing.Literal["segment.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + segments: typing.Optional[typing.List[Segment]] = pydantic.Field(default=None) + """ + A list of Segment objects + """ + + pages: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + A pagination object, which may be empty, indicating no further pages to fetch. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/single_filter_search_request.py b/src/intercom/unstable/types/single_filter_search_request.py new file mode 100644 index 00000000..94a3e417 --- /dev/null +++ b/src/intercom/unstable/types/single_filter_search_request.py @@ -0,0 +1,39 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .single_filter_search_request_operator import SingleFilterSearchRequestOperator +from .single_filter_search_request_value import SingleFilterSearchRequestValue + + +class SingleFilterSearchRequest(UncheckedBaseModel): + """ + Search using Intercoms Search APIs with a single filter. + """ + + field: typing.Optional[str] = pydantic.Field(default=None) + """ + The accepted field that you want to search on. + """ + + operator: typing.Optional[SingleFilterSearchRequestOperator] = pydantic.Field(default=None) + """ + The accepted operators you can use to define how you want to search for the value. + """ + + value: typing.Optional[SingleFilterSearchRequestValue] = pydantic.Field(default=None) + """ + The value that you want to search on. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/single_filter_search_request_operator.py b/src/intercom/unstable/types/single_filter_search_request_operator.py new file mode 100644 index 00000000..095ff011 --- /dev/null +++ b/src/intercom/unstable/types/single_filter_search_request_operator.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +SingleFilterSearchRequestOperator = typing.Union[typing.Literal["=", "!=", "IN", "NIN", "<", ">"], typing.Any] diff --git a/src/intercom/unstable/types/single_filter_search_request_value.py b/src/intercom/unstable/types/single_filter_search_request_value.py new file mode 100644 index 00000000..1ac348b9 --- /dev/null +++ b/src/intercom/unstable/types/single_filter_search_request_value.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .single_filter_search_request_value_two_item import SingleFilterSearchRequestValueTwoItem + +SingleFilterSearchRequestValue = typing.Union[str, int, typing.List[SingleFilterSearchRequestValueTwoItem]] diff --git a/src/intercom/unstable/types/single_filter_search_request_value_two_item.py b/src/intercom/unstable/types/single_filter_search_request_value_two_item.py new file mode 100644 index 00000000..e4b1cf4b --- /dev/null +++ b/src/intercom/unstable/types/single_filter_search_request_value_two_item.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +SingleFilterSearchRequestValueTwoItem = typing.Union[str, int] diff --git a/src/intercom/unstable/types/sla_applied.py b/src/intercom/unstable/types/sla_applied.py new file mode 100644 index 00000000..7a080506 --- /dev/null +++ b/src/intercom/unstable/types/sla_applied.py @@ -0,0 +1,42 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .sla_applied_sla_status import SlaAppliedSlaStatus + + +class SlaApplied(UncheckedBaseModel): + """ + The SLA Applied object contains the details for which SLA has been applied to this conversation. + Important: if there are any canceled sla_events for the conversation - meaning an SLA has been manually removed from a conversation, the sla_status will always be returned as null. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + object type + """ + + sla_name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the SLA as given by the teammate when it was created. + """ + + sla_status: typing.Optional[SlaAppliedSlaStatus] = pydantic.Field(default=None) + """ + SLA statuses: + - `hit`: If there’s at least one hit event in the underlying sla_events table, and no “missed” or “canceled” events for the conversation. + - `missed`: If there are any missed sla_events for the conversation and no canceled events. If there’s even a single missed sla event, the status will always be missed. A missed status is not applied when the SLA expires, only the next time a teammate replies. + - `active`: An SLA has been applied to a conversation, but has not yet been fulfilled. SLA status is active only if there are no “hit, “missed”, or “canceled” events. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/sla_applied_sla_status.py b/src/intercom/unstable/types/sla_applied_sla_status.py new file mode 100644 index 00000000..c2ad85af --- /dev/null +++ b/src/intercom/unstable/types/sla_applied_sla_status.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +SlaAppliedSlaStatus = typing.Union[typing.Literal["hit", "missed", "cancelled", "active"], typing.Any] diff --git a/src/intercom/unstable/types/snooze_conversation_request.py b/src/intercom/unstable/types/snooze_conversation_request.py new file mode 100644 index 00000000..15cafd19 --- /dev/null +++ b/src/intercom/unstable/types/snooze_conversation_request.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class SnoozeConversationRequest(UncheckedBaseModel): + """ + Payload of the request to snooze a conversation + """ + + admin_id: str = pydantic.Field() + """ + The id of the admin who is performing the action. + """ + + snoozed_until: int = pydantic.Field() + """ + The time you want the conversation to reopen. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/social_profile.py b/src/intercom/unstable/types/social_profile.py new file mode 100644 index 00000000..f4473ffa --- /dev/null +++ b/src/intercom/unstable/types/social_profile.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class SocialProfile(UncheckedBaseModel): + """ + A Social Profile allows you to label your contacts, companies, and conversations and list them using that Social Profile. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + value is "social_profile" + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the Social media profile + """ + + url: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the Social media profile + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/starting_after_paging.py b/src/intercom/unstable/types/starting_after_paging.py new file mode 100644 index 00000000..708069e5 --- /dev/null +++ b/src/intercom/unstable/types/starting_after_paging.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class StartingAfterPaging(UncheckedBaseModel): + per_page: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of results to fetch per page. + """ + + starting_after: typing.Optional[str] = pydantic.Field(default=None) + """ + The cursor to use in the next request to get the next page of results. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/subscription_type_list.py b/src/intercom/unstable/types/subscription_type_list.py new file mode 100644 index 00000000..1a1bbadb --- /dev/null +++ b/src/intercom/unstable/types/subscription_type_list.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..subscription_types.types.subscription_type import SubscriptionType + + +class SubscriptionTypeList(UncheckedBaseModel): + """ + A list of subscription type objects. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + data: typing.Optional[typing.List[SubscriptionType]] = pydantic.Field(default=None) + """ + A list of subscription type objects associated with the workspace . + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/tag_company_request.py b/src/intercom/unstable/types/tag_company_request.py new file mode 100644 index 00000000..56b5ef21 --- /dev/null +++ b/src/intercom/unstable/types/tag_company_request.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .tag_company_request_companies_item import TagCompanyRequestCompaniesItem + + +class TagCompanyRequest(UncheckedBaseModel): + """ + You can tag a single company or a list of companies. + """ + + name: str = pydantic.Field() + """ + The name of the tag, which will be created if not found. + """ + + companies: typing.List[TagCompanyRequestCompaniesItem] = pydantic.Field() + """ + The id or company_id of the company can be passed as input parameters. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/tag_company_request_companies_item.py b/src/intercom/unstable/types/tag_company_request_companies_item.py new file mode 100644 index 00000000..5b6d7d35 --- /dev/null +++ b/src/intercom/unstable/types/tag_company_request_companies_item.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class TagCompanyRequestCompaniesItem(UncheckedBaseModel): + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom defined id representing the company. + """ + + company_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The company id you have defined for the company. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/tag_list.py b/src/intercom/unstable/types/tag_list.py new file mode 100644 index 00000000..07f5af73 --- /dev/null +++ b/src/intercom/unstable/types/tag_list.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..tags.types.tag import Tag + + +class TagList(UncheckedBaseModel): + """ + A list of tags objects in the workspace. + """ + + type: typing.Optional[typing.Literal["list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + data: typing.Optional[typing.List[Tag]] = pydantic.Field(default=None) + """ + A list of tags objects associated with the workspace . + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/tag_multiple_users_request.py b/src/intercom/unstable/types/tag_multiple_users_request.py new file mode 100644 index 00000000..d95c6255 --- /dev/null +++ b/src/intercom/unstable/types/tag_multiple_users_request.py @@ -0,0 +1,30 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .tag_multiple_users_request_users_item import TagMultipleUsersRequestUsersItem + + +class TagMultipleUsersRequest(UncheckedBaseModel): + """ + You can tag a list of users. + """ + + name: str = pydantic.Field() + """ + The name of the tag, which will be created if not found. + """ + + users: typing.List[TagMultipleUsersRequestUsersItem] + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/tag_multiple_users_request_users_item.py b/src/intercom/unstable/types/tag_multiple_users_request_users_item.py new file mode 100644 index 00000000..06bc45d2 --- /dev/null +++ b/src/intercom/unstable/types/tag_multiple_users_request_users_item.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class TagMultipleUsersRequestUsersItem(UncheckedBaseModel): + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom defined id representing the user. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/tags.py b/src/intercom/unstable/types/tags.py new file mode 100644 index 00000000..9cb790b1 --- /dev/null +++ b/src/intercom/unstable/types/tags.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..tags.types.tag import Tag + + +class Tags(UncheckedBaseModel): + """ + A list of tags objects associated with a conversation + """ + + type: typing.Optional[typing.Literal["tag.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + tags: typing.Optional[typing.List[Tag]] = pydantic.Field(default=None) + """ + A list of tags objects associated with the conversation. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/team_list.py b/src/intercom/unstable/types/team_list.py new file mode 100644 index 00000000..a7f8ce68 --- /dev/null +++ b/src/intercom/unstable/types/team_list.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..teams.types.team import Team + + +class TeamList(UncheckedBaseModel): + """ + This will return a list of team objects for the App. + """ + + type: typing.Optional[typing.Literal["team.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + teams: typing.Optional[typing.List[Team]] = pydantic.Field(default=None) + """ + A list of team objects + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/team_priority_level.py b/src/intercom/unstable/types/team_priority_level.py new file mode 100644 index 00000000..1f0dafc7 --- /dev/null +++ b/src/intercom/unstable/types/team_priority_level.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class TeamPriorityLevel(UncheckedBaseModel): + """ + Admin priority levels for teams + """ + + primary_team_ids: typing.Optional[typing.List[int]] = pydantic.Field(default=None) + """ + The primary team ids for the team + """ + + secondary_team_ids: typing.Optional[typing.List[int]] = pydantic.Field(default=None) + """ + The secondary team ids for the team + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/ticket_custom_attributes.py b/src/intercom/unstable/types/ticket_custom_attributes.py new file mode 100644 index 00000000..bc03bf63 --- /dev/null +++ b/src/intercom/unstable/types/ticket_custom_attributes.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .ticket_custom_attributes_value import TicketCustomAttributesValue + +TicketCustomAttributes = typing.Dict[str, TicketCustomAttributesValue] diff --git a/src/intercom/unstable/types/ticket_custom_attributes_value.py b/src/intercom/unstable/types/ticket_custom_attributes_value.py new file mode 100644 index 00000000..886effc0 --- /dev/null +++ b/src/intercom/unstable/types/ticket_custom_attributes_value.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .file_attribute import FileAttribute + +TicketCustomAttributesValue = typing.Union[typing.Optional[str], float, bool, typing.List[typing.Any], FileAttribute] diff --git a/src/intercom/unstable/types/ticket_list.py b/src/intercom/unstable/types/ticket_list.py new file mode 100644 index 00000000..e1d1cba7 --- /dev/null +++ b/src/intercom/unstable/types/ticket_list.py @@ -0,0 +1,41 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..tickets.types.ticket import Ticket +from .cursor_pages import CursorPages + + +class TicketList(UncheckedBaseModel): + """ + Tickets are how you track requests from your users. + """ + + type: typing.Optional[typing.Literal["ticket.list"]] = pydantic.Field(default=None) + """ + Always ticket.list + """ + + tickets: typing.Optional[typing.List[typing.Optional[Ticket]]] = pydantic.Field(default=None) + """ + The list of ticket objects + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + A count of the total number of objects. + """ + + pages: typing.Optional[CursorPages] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/ticket_part_author.py b/src/intercom/unstable/types/ticket_part_author.py new file mode 100644 index 00000000..06ba68d1 --- /dev/null +++ b/src/intercom/unstable/types/ticket_part_author.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .ticket_part_author_type import TicketPartAuthorType + + +class TicketPartAuthor(UncheckedBaseModel): + """ + The author that wrote or triggered the part. Can be a bot, admin, team or user. + """ + + type: typing.Optional[TicketPartAuthorType] = pydantic.Field(default=None) + """ + The type of the author + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the author + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the author + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + The email of the author + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/ticket_part_author_type.py b/src/intercom/unstable/types/ticket_part_author_type.py new file mode 100644 index 00000000..1c4aa872 --- /dev/null +++ b/src/intercom/unstable/types/ticket_part_author_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketPartAuthorType = typing.Union[typing.Literal["admin", "bot", "team", "user"], typing.Any] diff --git a/src/intercom/unstable/types/ticket_parts.py b/src/intercom/unstable/types/ticket_parts.py new file mode 100644 index 00000000..5ea35083 --- /dev/null +++ b/src/intercom/unstable/types/ticket_parts.py @@ -0,0 +1,38 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..tickets.types.ticket_part import TicketPart + + +class TicketParts(UncheckedBaseModel): + """ + A list of Ticket Part objects for each note and event in the ticket. There is a limit of 500 parts. + """ + + type: typing.Optional[typing.Literal["ticket_part.list"]] = pydantic.Field(default=None) + """ + + """ + + ticket_parts: typing.Optional[typing.List[TicketPart]] = pydantic.Field(default=None) + """ + A list of Ticket Part objects for each ticket. There is a limit of 500 parts. + """ + + total_count: typing.Optional[int] = pydantic.Field(default=None) + """ + + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/ticket_reply.py b/src/intercom/unstable/types/ticket_reply.py new file mode 100644 index 00000000..920f5676 --- /dev/null +++ b/src/intercom/unstable/types/ticket_reply.py @@ -0,0 +1,66 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .part_attachment import PartAttachment +from .ticket_part_author import TicketPartAuthor +from .ticket_reply_part_type import TicketReplyPartType + + +class TicketReply(UncheckedBaseModel): + """ + A Ticket Part representing a note, comment, or quick_reply on a ticket + """ + + type: typing.Optional[typing.Literal["ticket_part"]] = pydantic.Field(default=None) + """ + Always ticket_part + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the part. + """ + + part_type: typing.Optional[TicketReplyPartType] = pydantic.Field(default=None) + """ + Type of the part + """ + + body: typing.Optional[str] = pydantic.Field(default=None) + """ + The message body, which may contain HTML. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the note was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The last time the note was updated. + """ + + author: typing.Optional[TicketPartAuthor] = None + attachments: typing.Optional[typing.List[PartAttachment]] = pydantic.Field(default=None) + """ + A list of attachments for the part. + """ + + redacted: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether or not the ticket part has been redacted. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/ticket_reply_part_type.py b/src/intercom/unstable/types/ticket_reply_part_type.py new file mode 100644 index 00000000..c7796a48 --- /dev/null +++ b/src/intercom/unstable/types/ticket_reply_part_type.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketReplyPartType = typing.Union[typing.Literal["note", "comment", "quick_reply"], typing.Any] diff --git a/src/intercom/unstable/types/ticket_request_custom_attributes.py b/src/intercom/unstable/types/ticket_request_custom_attributes.py new file mode 100644 index 00000000..10adb7d4 --- /dev/null +++ b/src/intercom/unstable/types/ticket_request_custom_attributes.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .ticket_request_custom_attributes_value import TicketRequestCustomAttributesValue + +TicketRequestCustomAttributes = typing.Dict[str, TicketRequestCustomAttributesValue] diff --git a/src/intercom/unstable/types/ticket_request_custom_attributes_value.py b/src/intercom/unstable/types/ticket_request_custom_attributes_value.py new file mode 100644 index 00000000..fce88bc0 --- /dev/null +++ b/src/intercom/unstable/types/ticket_request_custom_attributes_value.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TicketRequestCustomAttributesValue = typing.Union[typing.Optional[str], float, bool, typing.List[typing.Any]] diff --git a/src/intercom/unstable/types/ticket_state_list.py b/src/intercom/unstable/types/ticket_state_list.py new file mode 100644 index 00000000..34ef9e34 --- /dev/null +++ b/src/intercom/unstable/types/ticket_state_list.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..tickets.types.ticket_state_detailed import TicketStateDetailed + + +class TicketStateList(UncheckedBaseModel): + """ + A list of ticket states associated with a given ticket type. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `list`. + """ + + data: typing.Optional[typing.List[typing.Optional[TicketStateDetailed]]] = pydantic.Field(default=None) + """ + A list of ticket states associated with a given ticket type. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/ticket_type_attribute.py b/src/intercom/unstable/types/ticket_type_attribute.py new file mode 100644 index 00000000..dad8e9cf --- /dev/null +++ b/src/intercom/unstable/types/ticket_type_attribute.py @@ -0,0 +1,107 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class TicketTypeAttribute(UncheckedBaseModel): + """ + Ticket type attribute, used to define each data field to be captured in a ticket. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `ticket_type_attribute`. + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id representing the ticket type attribute. + """ + + workspace_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the workspace that the ticket type attribute belongs to. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the ticket type attribute + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + The description of the ticket type attribute + """ + + data_type: typing.Optional[str] = pydantic.Field(default=None) + """ + The type of the data attribute (allowed values: "string list integer decimal boolean datetime files") + """ + + input_options: typing.Optional[typing.Dict[str, typing.Any]] = pydantic.Field(default=None) + """ + Input options for the attribute + """ + + order: typing.Optional[int] = pydantic.Field(default=None) + """ + The order of the attribute against other attributes + """ + + required_to_create: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the attribute is required or not for teammates. + """ + + required_to_create_for_contacts: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the attribute is required or not for contacts. + """ + + visible_on_create: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the attribute is visible or not to teammates. + """ + + visible_to_contacts: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the attribute is visible or not to contacts. + """ + + default: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the attribute is built in or not. + """ + + ticket_type_id: typing.Optional[int] = pydantic.Field(default=None) + """ + The id of the ticket type that the attribute belongs to. + """ + + archived: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the ticket type attribute is archived or not. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The date and time the ticket type attribute was created. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The date and time the ticket type attribute was last updated. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/ticket_type_attribute_list.py b/src/intercom/unstable/types/ticket_type_attribute_list.py new file mode 100644 index 00000000..4578ce76 --- /dev/null +++ b/src/intercom/unstable/types/ticket_type_attribute_list.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .ticket_type_attribute import TicketTypeAttribute + + +class TicketTypeAttributeList(UncheckedBaseModel): + """ + A list of attributes associated with a given ticket type. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `ticket_type_attributes.list`. + """ + + ticket_type_attributes: typing.Optional[typing.List[typing.Optional[TicketTypeAttribute]]] = pydantic.Field( + default=None + ) + """ + A list of ticket type attributes associated with a given ticket type. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/ticket_type_list.py b/src/intercom/unstable/types/ticket_type_list.py new file mode 100644 index 00000000..79dd5bfe --- /dev/null +++ b/src/intercom/unstable/types/ticket_type_list.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..tickets.types.ticket_type import TicketType + + +class TicketTypeList(UncheckedBaseModel): + """ + A list of ticket types associated with a given workspace. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + String representing the object's type. Always has the value `list`. + """ + + data: typing.Optional[typing.List[typing.Optional[TicketType]]] = pydantic.Field(default=None) + """ + A list of ticket_types associated with a given workspace. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/translation.py b/src/intercom/unstable/types/translation.py new file mode 100644 index 00000000..2b8b3f30 --- /dev/null +++ b/src/intercom/unstable/types/translation.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class Translation(UncheckedBaseModel): + """ + A translation object contains the localised details of a subscription type. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The localised name of the subscription type. + """ + + description: typing.Optional[str] = pydantic.Field(default=None) + """ + The localised description of the subscription type. + """ + + locale: typing.Optional[str] = pydantic.Field(default=None) + """ + The two character identifier for the language of the translation object. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/untag_company_request.py b/src/intercom/unstable/types/untag_company_request.py new file mode 100644 index 00000000..2bc2f0ca --- /dev/null +++ b/src/intercom/unstable/types/untag_company_request.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .untag_company_request_companies_item import UntagCompanyRequestCompaniesItem + + +class UntagCompanyRequest(UncheckedBaseModel): + """ + You can tag a single company or a list of companies. + """ + + name: str = pydantic.Field() + """ + The name of the tag which will be untagged from the company + """ + + companies: typing.List[UntagCompanyRequestCompaniesItem] = pydantic.Field() + """ + The id or company_id of the company can be passed as input parameters. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/untag_company_request_companies_item.py b/src/intercom/unstable/types/untag_company_request_companies_item.py new file mode 100644 index 00000000..15dfb32b --- /dev/null +++ b/src/intercom/unstable/types/untag_company_request_companies_item.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class UntagCompanyRequestCompaniesItem(UncheckedBaseModel): + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom defined id representing the company. + """ + + company_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The company id you have defined for the company. + """ + + untag: typing.Optional[bool] = pydantic.Field(default=None) + """ + Always set to true + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/update_data_attribute_request_body.py b/src/intercom/unstable/types/update_data_attribute_request_body.py new file mode 100644 index 00000000..c7b69b7c --- /dev/null +++ b/src/intercom/unstable/types/update_data_attribute_request_body.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .update_data_attribute_request_options import UpdateDataAttributeRequestOptions + +UpdateDataAttributeRequestBody = typing.Union[UpdateDataAttributeRequestOptions, typing.Any] diff --git a/src/intercom/unstable/types/update_data_attribute_request_options.py b/src/intercom/unstable/types/update_data_attribute_request_options.py new file mode 100644 index 00000000..e74a340c --- /dev/null +++ b/src/intercom/unstable/types/update_data_attribute_request_options.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .update_data_attribute_request_options_options_item import UpdateDataAttributeRequestOptionsOptionsItem + + +class UpdateDataAttributeRequestOptions(UncheckedBaseModel): + options: typing.List[UpdateDataAttributeRequestOptionsOptionsItem] = pydantic.Field() + """ + Array of objects representing the options of the list, with `value` as the key and the option as the value. At least two options are required. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/update_data_attribute_request_options_options_item.py b/src/intercom/unstable/types/update_data_attribute_request_options_options_item.py new file mode 100644 index 00000000..30336763 --- /dev/null +++ b/src/intercom/unstable/types/update_data_attribute_request_options_options_item.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class UpdateDataAttributeRequestOptionsOptionsItem(UncheckedBaseModel): + value: typing.Optional[str] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/visitor.py b/src/intercom/unstable/types/visitor.py new file mode 100644 index 00000000..00ed964f --- /dev/null +++ b/src/intercom/unstable/types/visitor.py @@ -0,0 +1,169 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .visitor_avatar import VisitorAvatar +from .visitor_companies import VisitorCompanies +from .visitor_location_data import VisitorLocationData +from .visitor_segments import VisitorSegments +from .visitor_social_profiles import VisitorSocialProfiles +from .visitor_tags import VisitorTags + + +class Visitor(UncheckedBaseModel): + """ + Visitors are useful for representing anonymous people that have not yet been identified. They usually represent website visitors. Visitors are not visible in Intercom platform. The Visitors resource provides methods to fetch, update, convert and delete. + """ + + type: typing.Optional[str] = pydantic.Field(default=None) + """ + Value is 'visitor' + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Intercom defined id representing the Visitor. + """ + + user_id: typing.Optional[str] = pydantic.Field(default=None) + """ + Automatically generated identifier for the Visitor. + """ + + anonymous: typing.Optional[bool] = pydantic.Field(default=None) + """ + Identifies if this visitor is anonymous. + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + The email of the visitor. + """ + + phone: typing.Optional[str] = pydantic.Field(default=None) + """ + The phone number of the visitor. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the visitor. + """ + + pseudonym: typing.Optional[str] = pydantic.Field(default=None) + """ + The pseudonym of the visitor. + """ + + avatar: typing.Optional[VisitorAvatar] = None + app_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the app the visitor is associated with. + """ + + companies: typing.Optional[VisitorCompanies] = None + location_data: typing.Optional[VisitorLocationData] = None + las_request_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the Lead last recorded making a request. + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the Visitor was added to Intercom. + """ + + remote_created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the Visitor was added to Intercom. + """ + + signed_up_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The time the Visitor signed up for your product. + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + The last time the Visitor was updated. + """ + + session_count: typing.Optional[int] = pydantic.Field(default=None) + """ + The number of sessions the Visitor has had. + """ + + social_profiles: typing.Optional[VisitorSocialProfiles] = None + owner_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the admin that owns the Visitor. + """ + + unsubscribed_from_emails: typing.Optional[bool] = pydantic.Field(default=None) + """ + Whether the Visitor is unsubscribed from emails. + """ + + marked_email_as_spam: typing.Optional[bool] = pydantic.Field(default=None) + """ + Identifies if this visitor has marked an email as spam. + """ + + has_hard_bounced: typing.Optional[bool] = pydantic.Field(default=None) + """ + Identifies if this visitor has had a hard bounce. + """ + + tags: typing.Optional[VisitorTags] = None + segments: typing.Optional[VisitorSegments] = None + custom_attributes: typing.Optional[typing.Dict[str, str]] = pydantic.Field(default=None) + """ + The custom attributes you have set on the Visitor. + """ + + referrer: typing.Optional[str] = pydantic.Field(default=None) + """ + The referer of the visitor. + """ + + utm_campaign: typing.Optional[str] = pydantic.Field(default=None) + """ + The utm_campaign of the visitor. + """ + + utm_content: typing.Optional[str] = pydantic.Field(default=None) + """ + The utm_content of the visitor. + """ + + utm_medium: typing.Optional[str] = pydantic.Field(default=None) + """ + The utm_medium of the visitor. + """ + + utm_source: typing.Optional[str] = pydantic.Field(default=None) + """ + The utm_source of the visitor. + """ + + utm_term: typing.Optional[str] = pydantic.Field(default=None) + """ + The utm_term of the visitor. + """ + + do_not_track: typing.Optional[bool] = pydantic.Field(default=None) + """ + Identifies if this visitor has do not track enabled. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/visitor_avatar.py b/src/intercom/unstable/types/visitor_avatar.py new file mode 100644 index 00000000..9bf0b256 --- /dev/null +++ b/src/intercom/unstable/types/visitor_avatar.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class VisitorAvatar(UncheckedBaseModel): + type: typing.Optional[str] = pydantic.Field(default=None) + """ + + """ + + image_url: typing.Optional[str] = pydantic.Field(default=None) + """ + This object represents the avatar associated with the visitor. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/visitor_companies.py b/src/intercom/unstable/types/visitor_companies.py new file mode 100644 index 00000000..8fd91897 --- /dev/null +++ b/src/intercom/unstable/types/visitor_companies.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from ..companies.types.company import Company + + +class VisitorCompanies(UncheckedBaseModel): + type: typing.Optional[typing.Literal["company.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + companies: typing.Optional[typing.List[Company]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/visitor_deleted_object.py b/src/intercom/unstable/types/visitor_deleted_object.py new file mode 100644 index 00000000..dd6b98e3 --- /dev/null +++ b/src/intercom/unstable/types/visitor_deleted_object.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class VisitorDeletedObject(UncheckedBaseModel): + """ + Response returned when an object is deleted + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The unique identifier for the visitor which is given by Intercom. + """ + + type: typing.Optional[typing.Literal["visitor"]] = pydantic.Field(default=None) + """ + The type of object which was deleted + """ + + user_id: typing.Optional[str] = pydantic.Field(default=None) + """ + Automatically generated identifier for the Visitor. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/visitor_location_data.py b/src/intercom/unstable/types/visitor_location_data.py new file mode 100644 index 00000000..a6819561 --- /dev/null +++ b/src/intercom/unstable/types/visitor_location_data.py @@ -0,0 +1,58 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class VisitorLocationData(UncheckedBaseModel): + type: typing.Optional[str] = pydantic.Field(default=None) + """ + + """ + + city_name: typing.Optional[str] = pydantic.Field(default=None) + """ + The city name of the visitor. + """ + + continent_code: typing.Optional[str] = pydantic.Field(default=None) + """ + The continent code of the visitor. + """ + + country_code: typing.Optional[str] = pydantic.Field(default=None) + """ + The country code of the visitor. + """ + + country_name: typing.Optional[str] = pydantic.Field(default=None) + """ + The country name of the visitor. + """ + + postal_code: typing.Optional[str] = pydantic.Field(default=None) + """ + The postal code of the visitor. + """ + + region_name: typing.Optional[str] = pydantic.Field(default=None) + """ + The region name of the visitor. + """ + + timezone: typing.Optional[str] = pydantic.Field(default=None) + """ + The timezone of the visitor. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/visitor_segments.py b/src/intercom/unstable/types/visitor_segments.py new file mode 100644 index 00000000..2e8bb86f --- /dev/null +++ b/src/intercom/unstable/types/visitor_segments.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class VisitorSegments(UncheckedBaseModel): + type: typing.Optional[typing.Literal["segment.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + segments: typing.Optional[typing.List[str]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/visitor_social_profiles.py b/src/intercom/unstable/types/visitor_social_profiles.py new file mode 100644 index 00000000..c8c56be6 --- /dev/null +++ b/src/intercom/unstable/types/visitor_social_profiles.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class VisitorSocialProfiles(UncheckedBaseModel): + type: typing.Optional[typing.Literal["social_profile.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + social_profiles: typing.Optional[typing.List[str]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/visitor_tags.py b/src/intercom/unstable/types/visitor_tags.py new file mode 100644 index 00000000..160a8e4a --- /dev/null +++ b/src/intercom/unstable/types/visitor_tags.py @@ -0,0 +1,26 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .visitor_tags_tags_item import VisitorTagsTagsItem + + +class VisitorTags(UncheckedBaseModel): + type: typing.Optional[typing.Literal["tag.list"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + tags: typing.Optional[typing.List[VisitorTagsTagsItem]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/visitor_tags_tags_item.py b/src/intercom/unstable/types/visitor_tags_tags_item.py new file mode 100644 index 00000000..17d5a21a --- /dev/null +++ b/src/intercom/unstable/types/visitor_tags_tags_item.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class VisitorTagsTagsItem(UncheckedBaseModel): + type: typing.Optional[typing.Literal["tag"]] = pydantic.Field(default=None) + """ + The type of the object + """ + + id: typing.Optional[str] = pydantic.Field(default=None) + """ + The id of the tag. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name of the tag. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/whatsapp_message_status_list.py b/src/intercom/unstable/types/whatsapp_message_status_list.py new file mode 100644 index 00000000..7681a8a1 --- /dev/null +++ b/src/intercom/unstable/types/whatsapp_message_status_list.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .whatsapp_message_status_list_events_item import WhatsappMessageStatusListEventsItem +from .whatsapp_message_status_list_pages import WhatsappMessageStatusListPages + + +class WhatsappMessageStatusList(UncheckedBaseModel): + type: typing.Literal["list"] = "list" + ruleset_id: str = pydantic.Field() + """ + The provided ruleset ID + """ + + pages: WhatsappMessageStatusListPages + total_count: int = pydantic.Field() + """ + Total number of events + """ + + events: typing.List[WhatsappMessageStatusListEventsItem] + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/whatsapp_message_status_list_events_item.py b/src/intercom/unstable/types/whatsapp_message_status_list_events_item.py new file mode 100644 index 00000000..669f09d2 --- /dev/null +++ b/src/intercom/unstable/types/whatsapp_message_status_list_events_item.py @@ -0,0 +1,59 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .whatsapp_message_status_list_events_item_status import WhatsappMessageStatusListEventsItemStatus + + +class WhatsappMessageStatusListEventsItem(UncheckedBaseModel): + id: str = pydantic.Field() + """ + Event ID + """ + + conversation_id: str = pydantic.Field() + """ + ID of the conversation + """ + + status: WhatsappMessageStatusListEventsItemStatus = pydantic.Field() + """ + Current status of the message + """ + + type: typing.Literal["broadcast_outbound"] = pydantic.Field(default="broadcast_outbound") + """ + Event type + """ + + created_at: int = pydantic.Field() + """ + Creation timestamp + """ + + updated_at: int = pydantic.Field() + """ + Last update timestamp + """ + + whatsapp_message_id: str = pydantic.Field() + """ + WhatsApp's message identifier + """ + + template_name: typing.Optional[str] = pydantic.Field(default=None) + """ + Name of the WhatsApp template used + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/whatsapp_message_status_list_events_item_status.py b/src/intercom/unstable/types/whatsapp_message_status_list_events_item_status.py new file mode 100644 index 00000000..3db079a1 --- /dev/null +++ b/src/intercom/unstable/types/whatsapp_message_status_list_events_item_status.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +WhatsappMessageStatusListEventsItemStatus = typing.Union[ + typing.Literal["sent", "delivered", "read", "failed"], typing.Any +] diff --git a/src/intercom/unstable/types/whatsapp_message_status_list_pages.py b/src/intercom/unstable/types/whatsapp_message_status_list_pages.py new file mode 100644 index 00000000..123971be --- /dev/null +++ b/src/intercom/unstable/types/whatsapp_message_status_list_pages.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel +from .whatsapp_message_status_list_pages_next import WhatsappMessageStatusListPagesNext + + +class WhatsappMessageStatusListPages(UncheckedBaseModel): + type: typing.Literal["pages"] = "pages" + per_page: int = pydantic.Field() + """ + Number of results per page + """ + + total_pages: int = pydantic.Field() + """ + Total number of pages + """ + + next: typing.Optional[WhatsappMessageStatusListPagesNext] = pydantic.Field(default=None) + """ + Information for fetching next page (null if no more pages) + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/types/whatsapp_message_status_list_pages_next.py b/src/intercom/unstable/types/whatsapp_message_status_list_pages_next.py new file mode 100644 index 00000000..5cfc3d9c --- /dev/null +++ b/src/intercom/unstable/types/whatsapp_message_status_list_pages_next.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class WhatsappMessageStatusListPagesNext(UncheckedBaseModel): + """ + Information for fetching next page (null if no more pages) + """ + + starting_after: typing.Optional[str] = pydantic.Field(default=None) + """ + Cursor for the next page + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/unstable/visitors/__init__.py b/src/intercom/unstable/visitors/__init__.py new file mode 100644 index 00000000..5cde0202 --- /dev/null +++ b/src/intercom/unstable/visitors/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/src/intercom/unstable/visitors/client.py b/src/intercom/unstable/visitors/client.py new file mode 100644 index 00000000..a8e8c619 --- /dev/null +++ b/src/intercom/unstable/visitors/client.py @@ -0,0 +1,309 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ...types.update_visitor_request_one import UpdateVisitorRequestOne +from ..contacts.types.contact import Contact +from ..types.visitor import Visitor +from .raw_client import AsyncRawVisitorsClient, RawVisitorsClient + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class VisitorsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawVisitorsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawVisitorsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawVisitorsClient + """ + return self._raw_client + + def retrieve_visitor_with_user_id( + self, *, user_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[Visitor]: + """ + You can fetch the details of a single visitor. + + Parameters + ---------- + user_id : str + The user_id of the Visitor you want to retrieve. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Visitor] + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.visitors.retrieve_visitor_with_user_id( + user_id="user_id", + ) + """ + _response = self._raw_client.retrieve_visitor_with_user_id(user_id=user_id, request_options=request_options) + return _response.data + + def update_visitor( + self, *, request: UpdateVisitorRequestOne, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[Visitor]: + """ + Sending a PUT request to `/visitors` will result in an update of an existing Visitor. + + **Option 1.** You can update a visitor by passing in the `user_id` of the visitor in the Request body. + + **Option 2.** You can update a visitor by passing in the `id` of the visitor in the Request body. + + Parameters + ---------- + request : UpdateVisitorRequestOne + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Visitor] + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.visitors.update_visitor( + request={"id": "6762f30c1bb69f9f2193bc5e", "name": "Gareth Bale"}, + ) + """ + _response = self._raw_client.update_visitor(request=request, request_options=request_options) + return _response.data + + def convert_visitor( + self, + *, + type: str, + user: typing.Any, + visitor: typing.Any, + request_options: typing.Optional[RequestOptions] = None, + ) -> Contact: + """ + You can merge a Visitor to a Contact of role type `lead` or `user`. + + > 📘 What happens upon a visitor being converted? + > + > If the User exists, then the Visitor will be merged into it, the Visitor deleted and the User returned. If the User does not exist, the Visitor will be converted to a User, with the User identifiers replacing it's Visitor identifiers. + + Parameters + ---------- + type : str + Represents the role of the Contact model. Accepts `lead` or `user`. + + user : typing.Any + + visitor : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Contact + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.unstable.visitors.convert_visitor( + type="user", + user={"email": "foo@bar.com"}, + visitor={"user_id": "3ecf64d0-9ed1-4e9f-88e1-da7d6e6782f3"}, + ) + """ + _response = self._raw_client.convert_visitor( + type=type, user=user, visitor=visitor, request_options=request_options + ) + return _response.data + + +class AsyncVisitorsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawVisitorsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawVisitorsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawVisitorsClient + """ + return self._raw_client + + async def retrieve_visitor_with_user_id( + self, *, user_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[Visitor]: + """ + You can fetch the details of a single visitor. + + Parameters + ---------- + user_id : str + The user_id of the Visitor you want to retrieve. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Visitor] + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.visitors.retrieve_visitor_with_user_id( + user_id="user_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.retrieve_visitor_with_user_id( + user_id=user_id, request_options=request_options + ) + return _response.data + + async def update_visitor( + self, *, request: UpdateVisitorRequestOne, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[Visitor]: + """ + Sending a PUT request to `/visitors` will result in an update of an existing Visitor. + + **Option 1.** You can update a visitor by passing in the `user_id` of the visitor in the Request body. + + **Option 2.** You can update a visitor by passing in the `id` of the visitor in the Request body. + + Parameters + ---------- + request : UpdateVisitorRequestOne + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Visitor] + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.visitors.update_visitor( + request={"id": "6762f30c1bb69f9f2193bc5e", "name": "Gareth Bale"}, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update_visitor(request=request, request_options=request_options) + return _response.data + + async def convert_visitor( + self, + *, + type: str, + user: typing.Any, + visitor: typing.Any, + request_options: typing.Optional[RequestOptions] = None, + ) -> Contact: + """ + You can merge a Visitor to a Contact of role type `lead` or `user`. + + > 📘 What happens upon a visitor being converted? + > + > If the User exists, then the Visitor will be merged into it, the Visitor deleted and the User returned. If the User does not exist, the Visitor will be converted to a User, with the User identifiers replacing it's Visitor identifiers. + + Parameters + ---------- + type : str + Represents the role of the Contact model. Accepts `lead` or `user`. + + user : typing.Any + + visitor : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Contact + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.unstable.visitors.convert_visitor( + type="user", + user={"email": "foo@bar.com"}, + visitor={"user_id": "3ecf64d0-9ed1-4e9f-88e1-da7d6e6782f3"}, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.convert_visitor( + type=type, user=user, visitor=visitor, request_options=request_options + ) + return _response.data diff --git a/src/intercom/unstable/visitors/raw_client.py b/src/intercom/unstable/visitors/raw_client.py new file mode 100644 index 00000000..aadac321 --- /dev/null +++ b/src/intercom/unstable/visitors/raw_client.py @@ -0,0 +1,447 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.http_response import AsyncHttpResponse, HttpResponse +from ...core.request_options import RequestOptions +from ...core.unchecked_base_model import construct_type +from ...types.update_visitor_request_one import UpdateVisitorRequestOne +from ..contacts.types.contact import Contact +from ..errors.not_found_error import NotFoundError +from ..errors.unauthorized_error import UnauthorizedError +from ..types.error import Error +from ..types.visitor import Visitor + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawVisitorsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def retrieve_visitor_with_user_id( + self, *, user_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[typing.Optional[Visitor]]: + """ + You can fetch the details of a single visitor. + + Parameters + ---------- + user_id : str + The user_id of the Visitor you want to retrieve. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[Visitor]] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "visitors", + method="GET", + params={ + "user_id": user_id, + }, + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Visitor], + construct_type( + type_=typing.Optional[Visitor], # 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 update_visitor( + self, *, request: UpdateVisitorRequestOne, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[typing.Optional[Visitor]]: + """ + Sending a PUT request to `/visitors` will result in an update of an existing Visitor. + + **Option 1.** You can update a visitor by passing in the `user_id` of the visitor in the Request body. + + **Option 2.** You can update a visitor by passing in the `id` of the visitor in the Request body. + + Parameters + ---------- + request : UpdateVisitorRequestOne + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[Visitor]] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "visitors", + method="PUT", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Visitor], + construct_type( + type_=typing.Optional[Visitor], # 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 convert_visitor( + self, + *, + type: str, + user: typing.Any, + visitor: typing.Any, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Contact]: + """ + You can merge a Visitor to a Contact of role type `lead` or `user`. + + > 📘 What happens upon a visitor being converted? + > + > If the User exists, then the Visitor will be merged into it, the Visitor deleted and the User returned. If the User does not exist, the Visitor will be converted to a User, with the User identifiers replacing it's Visitor identifiers. + + Parameters + ---------- + type : str + Represents the role of the Contact model. Accepts `lead` or `user`. + + user : typing.Any + + visitor : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Contact] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "visitors/convert", + method="POST", + json={ + "type": type, + "user": user, + "visitor": visitor, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Contact, + construct_type( + type_=Contact, # 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) + + +class AsyncRawVisitorsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def retrieve_visitor_with_user_id( + self, *, user_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[typing.Optional[Visitor]]: + """ + You can fetch the details of a single visitor. + + Parameters + ---------- + user_id : str + The user_id of the Visitor you want to retrieve. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[Visitor]] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "visitors", + method="GET", + params={ + "user_id": user_id, + }, + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Visitor], + construct_type( + type_=typing.Optional[Visitor], # 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 update_visitor( + self, *, request: UpdateVisitorRequestOne, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[typing.Optional[Visitor]]: + """ + Sending a PUT request to `/visitors` will result in an update of an existing Visitor. + + **Option 1.** You can update a visitor by passing in the `user_id` of the visitor in the Request body. + + **Option 2.** You can update a visitor by passing in the `id` of the visitor in the Request body. + + Parameters + ---------- + request : UpdateVisitorRequestOne + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[Visitor]] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "visitors", + method="PUT", + json=request, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Visitor], + construct_type( + type_=typing.Optional[Visitor], # 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 convert_visitor( + self, + *, + type: str, + user: typing.Any, + visitor: typing.Any, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Contact]: + """ + You can merge a Visitor to a Contact of role type `lead` or `user`. + + > 📘 What happens upon a visitor being converted? + > + > If the User exists, then the Visitor will be merged into it, the Visitor deleted and the User returned. If the User does not exist, the Visitor will be converted to a User, with the User identifiers replacing it's Visitor identifiers. + + Parameters + ---------- + type : str + Represents the role of the Contact model. Accepts `lead` or `user`. + + user : typing.Any + + visitor : typing.Any + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Contact] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "visitors/convert", + method="POST", + json={ + "type": type, + "user": user, + "visitor": visitor, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Contact, + construct_type( + type_=Contact, # 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) diff --git a/src/intercom/version.py b/src/intercom/version.py new file mode 100644 index 00000000..a916f83a --- /dev/null +++ b/src/intercom/version.py @@ -0,0 +1,3 @@ +from importlib import metadata + +__version__ = metadata.version("python-intercom") diff --git a/src/intercom/visitors/__init__.py b/src/intercom/visitors/__init__.py new file mode 100644 index 00000000..5dde877d --- /dev/null +++ b/src/intercom/visitors/__init__.py @@ -0,0 +1,58 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .types import ( + ConvertVisitorRequestUser, + ConvertVisitorRequestVisitor, + UserWithId, + UserWithUserId, + VisitorWithEmail, + VisitorWithId, + VisitorWithUserId, + ) +_dynamic_imports: typing.Dict[str, str] = { + "ConvertVisitorRequestUser": ".types", + "ConvertVisitorRequestVisitor": ".types", + "UserWithId": ".types", + "UserWithUserId": ".types", + "VisitorWithEmail": ".types", + "VisitorWithId": ".types", + "VisitorWithUserId": ".types", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "ConvertVisitorRequestUser", + "ConvertVisitorRequestVisitor", + "UserWithId", + "UserWithUserId", + "VisitorWithEmail", + "VisitorWithId", + "VisitorWithUserId", +] diff --git a/src/intercom/visitors/client.py b/src/intercom/visitors/client.py new file mode 100644 index 00000000..5ae6c04d --- /dev/null +++ b/src/intercom/visitors/client.py @@ -0,0 +1,331 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ..contacts.types.contact import Contact +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.request_options import RequestOptions +from ..types.update_visitor_request import UpdateVisitorRequest +from ..types.visitor import Visitor +from .raw_client import AsyncRawVisitorsClient, RawVisitorsClient +from .types.convert_visitor_request_user import ConvertVisitorRequestUser +from .types.convert_visitor_request_visitor import ConvertVisitorRequestVisitor + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class VisitorsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawVisitorsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawVisitorsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawVisitorsClient + """ + return self._raw_client + + def find( + self, *, user_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[Visitor]: + """ + You can fetch the details of a single visitor. + + Parameters + ---------- + user_id : str + The user_id of the Visitor you want to retrieve. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Visitor] + successful + + Examples + -------- + from intercom import Intercom + + client = Intercom( + token="YOUR_TOKEN", + ) + client.visitors.find( + user_id="user_id", + ) + """ + _response = self._raw_client.find(user_id=user_id, request_options=request_options) + return _response.data + + def update( + self, *, request: UpdateVisitorRequest, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[Visitor]: + """ + Sending a PUT request to `/visitors` will result in an update of an existing Visitor. + + **Option 1.** You can update a visitor by passing in the `user_id` of the visitor in the Request body. + + **Option 2.** You can update a visitor by passing in the `id` of the visitor in the Request body. + + Parameters + ---------- + request : UpdateVisitorRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Visitor] + successful + + Examples + -------- + from intercom import Intercom, UpdateVisitorRequestWithId + + client = Intercom( + token="YOUR_TOKEN", + ) + client.visitors.update( + request=UpdateVisitorRequestWithId( + id="6762f30c1bb69f9f2193bc5e", + name="Gareth Bale", + ), + ) + """ + _response = self._raw_client.update(request=request, request_options=request_options) + return _response.data + + def merge_to_contact( + self, + *, + type: str, + user: ConvertVisitorRequestUser, + visitor: ConvertVisitorRequestVisitor, + request_options: typing.Optional[RequestOptions] = None, + ) -> Contact: + """ + You can merge a Visitor to a Contact of role type `lead` or `user`. + + > 📘 What happens upon a visitor being converted? + > + > If the User exists, then the Visitor will be merged into it, the Visitor deleted and the User returned. If the User does not exist, the Visitor will be converted to a User, with the User identifiers replacing it's Visitor identifiers. + + Parameters + ---------- + type : str + Represents the role of the Contact model. Accepts `lead` or `user`. + + user : ConvertVisitorRequestUser + The unique identifiers retained after converting or merging. + + visitor : ConvertVisitorRequestVisitor + The unique identifiers to convert a single Visitor. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Contact + successful + + Examples + -------- + from intercom import Intercom + from intercom.visitors import UserWithId, VisitorWithUserId + + client = Intercom( + token="YOUR_TOKEN", + ) + client.visitors.merge_to_contact( + type="user", + user=UserWithId( + id="8a88a590-e1c3-41e2-a502-e0649dbf721c", + email="foo@bar.com", + ), + visitor=VisitorWithUserId( + user_id="3ecf64d0-9ed1-4e9f-88e1-da7d6e6782f3", + ), + ) + """ + _response = self._raw_client.merge_to_contact( + type=type, user=user, visitor=visitor, request_options=request_options + ) + return _response.data + + +class AsyncVisitorsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawVisitorsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawVisitorsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawVisitorsClient + """ + return self._raw_client + + async def find( + self, *, user_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[Visitor]: + """ + You can fetch the details of a single visitor. + + Parameters + ---------- + user_id : str + The user_id of the Visitor you want to retrieve. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Visitor] + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.visitors.find( + user_id="user_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.find(user_id=user_id, request_options=request_options) + return _response.data + + async def update( + self, *, request: UpdateVisitorRequest, request_options: typing.Optional[RequestOptions] = None + ) -> typing.Optional[Visitor]: + """ + Sending a PUT request to `/visitors` will result in an update of an existing Visitor. + + **Option 1.** You can update a visitor by passing in the `user_id` of the visitor in the Request body. + + **Option 2.** You can update a visitor by passing in the `id` of the visitor in the Request body. + + Parameters + ---------- + request : UpdateVisitorRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + typing.Optional[Visitor] + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom, UpdateVisitorRequestWithId + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.visitors.update( + request=UpdateVisitorRequestWithId( + id="6762f30c1bb69f9f2193bc5e", + name="Gareth Bale", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.update(request=request, request_options=request_options) + return _response.data + + async def merge_to_contact( + self, + *, + type: str, + user: ConvertVisitorRequestUser, + visitor: ConvertVisitorRequestVisitor, + request_options: typing.Optional[RequestOptions] = None, + ) -> Contact: + """ + You can merge a Visitor to a Contact of role type `lead` or `user`. + + > 📘 What happens upon a visitor being converted? + > + > If the User exists, then the Visitor will be merged into it, the Visitor deleted and the User returned. If the User does not exist, the Visitor will be converted to a User, with the User identifiers replacing it's Visitor identifiers. + + Parameters + ---------- + type : str + Represents the role of the Contact model. Accepts `lead` or `user`. + + user : ConvertVisitorRequestUser + The unique identifiers retained after converting or merging. + + visitor : ConvertVisitorRequestVisitor + The unique identifiers to convert a single Visitor. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + Contact + successful + + Examples + -------- + import asyncio + + from intercom import AsyncIntercom + from intercom.visitors import UserWithId, VisitorWithUserId + + client = AsyncIntercom( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.visitors.merge_to_contact( + type="user", + user=UserWithId( + id="8a88a590-e1c3-41e2-a502-e0649dbf721c", + email="foo@bar.com", + ), + visitor=VisitorWithUserId( + user_id="3ecf64d0-9ed1-4e9f-88e1-da7d6e6782f3", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.merge_to_contact( + type=type, user=user, visitor=visitor, request_options=request_options + ) + return _response.data diff --git a/src/intercom/visitors/raw_client.py b/src/intercom/visitors/raw_client.py new file mode 100644 index 00000000..c5983644 --- /dev/null +++ b/src/intercom/visitors/raw_client.py @@ -0,0 +1,466 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ..contacts.types.contact import Contact +from ..core.api_error import ApiError +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ..core.http_response import AsyncHttpResponse, HttpResponse +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 ..types.error import Error +from ..types.update_visitor_request import UpdateVisitorRequest +from ..types.visitor import Visitor +from .types.convert_visitor_request_user import ConvertVisitorRequestUser +from .types.convert_visitor_request_visitor import ConvertVisitorRequestVisitor + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawVisitorsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def find( + self, *, user_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[typing.Optional[Visitor]]: + """ + You can fetch the details of a single visitor. + + Parameters + ---------- + user_id : str + The user_id of the Visitor you want to retrieve. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[Visitor]] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "visitors", + method="GET", + params={ + "user_id": user_id, + }, + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Visitor], + construct_type( + type_=typing.Optional[Visitor], # 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 update( + self, *, request: UpdateVisitorRequest, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[typing.Optional[Visitor]]: + """ + Sending a PUT request to `/visitors` will result in an update of an existing Visitor. + + **Option 1.** You can update a visitor by passing in the `user_id` of the visitor in the Request body. + + **Option 2.** You can update a visitor by passing in the `id` of the visitor in the Request body. + + Parameters + ---------- + request : UpdateVisitorRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[typing.Optional[Visitor]] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "visitors", + method="PUT", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=UpdateVisitorRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return HttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Visitor], + construct_type( + type_=typing.Optional[Visitor], # 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 merge_to_contact( + self, + *, + type: str, + user: ConvertVisitorRequestUser, + visitor: ConvertVisitorRequestVisitor, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[Contact]: + """ + You can merge a Visitor to a Contact of role type `lead` or `user`. + + > 📘 What happens upon a visitor being converted? + > + > If the User exists, then the Visitor will be merged into it, the Visitor deleted and the User returned. If the User does not exist, the Visitor will be converted to a User, with the User identifiers replacing it's Visitor identifiers. + + Parameters + ---------- + type : str + Represents the role of the Contact model. Accepts `lead` or `user`. + + user : ConvertVisitorRequestUser + The unique identifiers retained after converting or merging. + + visitor : ConvertVisitorRequestVisitor + The unique identifiers to convert a single Visitor. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[Contact] + successful + """ + _response = self._client_wrapper.httpx_client.request( + "visitors/convert", + method="POST", + json={ + "type": type, + "user": convert_and_respect_annotation_metadata( + object_=user, annotation=ConvertVisitorRequestUser, direction="write" + ), + "visitor": convert_and_respect_annotation_metadata( + object_=visitor, annotation=ConvertVisitorRequestVisitor, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Contact, + construct_type( + type_=Contact, # 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) + + +class AsyncRawVisitorsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def find( + self, *, user_id: str, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[typing.Optional[Visitor]]: + """ + You can fetch the details of a single visitor. + + Parameters + ---------- + user_id : str + The user_id of the Visitor you want to retrieve. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[Visitor]] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "visitors", + method="GET", + params={ + "user_id": user_id, + }, + request_options=request_options, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Visitor], + construct_type( + type_=typing.Optional[Visitor], # 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 update( + self, *, request: UpdateVisitorRequest, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[typing.Optional[Visitor]]: + """ + Sending a PUT request to `/visitors` will result in an update of an existing Visitor. + + **Option 1.** You can update a visitor by passing in the `user_id` of the visitor in the Request body. + + **Option 2.** You can update a visitor by passing in the `id` of the visitor in the Request body. + + Parameters + ---------- + request : UpdateVisitorRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[typing.Optional[Visitor]] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "visitors", + method="PUT", + json=convert_and_respect_annotation_metadata( + object_=request, annotation=UpdateVisitorRequest, direction="write" + ), + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if _response is None or not _response.text.strip(): + return AsyncHttpResponse(response=_response, data=None) + if 200 <= _response.status_code < 300: + _data = typing.cast( + typing.Optional[Visitor], + construct_type( + type_=typing.Optional[Visitor], # 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 merge_to_contact( + self, + *, + type: str, + user: ConvertVisitorRequestUser, + visitor: ConvertVisitorRequestVisitor, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[Contact]: + """ + You can merge a Visitor to a Contact of role type `lead` or `user`. + + > 📘 What happens upon a visitor being converted? + > + > If the User exists, then the Visitor will be merged into it, the Visitor deleted and the User returned. If the User does not exist, the Visitor will be converted to a User, with the User identifiers replacing it's Visitor identifiers. + + Parameters + ---------- + type : str + Represents the role of the Contact model. Accepts `lead` or `user`. + + user : ConvertVisitorRequestUser + The unique identifiers retained after converting or merging. + + visitor : ConvertVisitorRequestVisitor + The unique identifiers to convert a single Visitor. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[Contact] + successful + """ + _response = await self._client_wrapper.httpx_client.request( + "visitors/convert", + method="POST", + json={ + "type": type, + "user": convert_and_respect_annotation_metadata( + object_=user, annotation=ConvertVisitorRequestUser, direction="write" + ), + "visitor": convert_and_respect_annotation_metadata( + object_=visitor, annotation=ConvertVisitorRequestVisitor, direction="write" + ), + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + Contact, + construct_type( + type_=Contact, # 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) diff --git a/src/intercom/visitors/types/__init__.py b/src/intercom/visitors/types/__init__.py new file mode 100644 index 00000000..861e6dfa --- /dev/null +++ b/src/intercom/visitors/types/__init__.py @@ -0,0 +1,56 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from .convert_visitor_request_user import ConvertVisitorRequestUser + from .convert_visitor_request_visitor import ConvertVisitorRequestVisitor + from .user_with_id import UserWithId + from .user_with_user_id import UserWithUserId + from .visitor_with_email import VisitorWithEmail + from .visitor_with_id import VisitorWithId + from .visitor_with_user_id import VisitorWithUserId +_dynamic_imports: typing.Dict[str, str] = { + "ConvertVisitorRequestUser": ".convert_visitor_request_user", + "ConvertVisitorRequestVisitor": ".convert_visitor_request_visitor", + "UserWithId": ".user_with_id", + "UserWithUserId": ".user_with_user_id", + "VisitorWithEmail": ".visitor_with_email", + "VisitorWithId": ".visitor_with_id", + "VisitorWithUserId": ".visitor_with_user_id", +} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = [ + "ConvertVisitorRequestUser", + "ConvertVisitorRequestVisitor", + "UserWithId", + "UserWithUserId", + "VisitorWithEmail", + "VisitorWithId", + "VisitorWithUserId", +] diff --git a/src/intercom/visitors/types/convert_visitor_request_user.py b/src/intercom/visitors/types/convert_visitor_request_user.py new file mode 100644 index 00000000..b0812cc9 --- /dev/null +++ b/src/intercom/visitors/types/convert_visitor_request_user.py @@ -0,0 +1,8 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .user_with_id import UserWithId +from .user_with_user_id import UserWithUserId + +ConvertVisitorRequestUser = typing.Union[UserWithId, UserWithUserId] diff --git a/src/intercom/visitors/types/convert_visitor_request_visitor.py b/src/intercom/visitors/types/convert_visitor_request_visitor.py new file mode 100644 index 00000000..7903e59c --- /dev/null +++ b/src/intercom/visitors/types/convert_visitor_request_visitor.py @@ -0,0 +1,9 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .visitor_with_email import VisitorWithEmail +from .visitor_with_id import VisitorWithId +from .visitor_with_user_id import VisitorWithUserId + +ConvertVisitorRequestVisitor = typing.Union[VisitorWithId, VisitorWithUserId, VisitorWithEmail] diff --git a/src/intercom/visitors/types/user_with_id.py b/src/intercom/visitors/types/user_with_id.py new file mode 100644 index 00000000..972c56e0 --- /dev/null +++ b/src/intercom/visitors/types/user_with_id.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class UserWithId(UncheckedBaseModel): + id: str = pydantic.Field() + """ + The unique identifier for the contact which is given by Intercom. + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + The contact's email, retained by default if one is present. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/visitors/types/user_with_user_id.py b/src/intercom/visitors/types/user_with_user_id.py new file mode 100644 index 00000000..8c8c5580 --- /dev/null +++ b/src/intercom/visitors/types/user_with_user_id.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class UserWithUserId(UncheckedBaseModel): + user_id: str = pydantic.Field() + """ + A unique identifier for the contact which is given to Intercom. + """ + + email: typing.Optional[str] = pydantic.Field(default=None) + """ + The contact's email, retained by default if one is present. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/visitors/types/visitor_with_email.py b/src/intercom/visitors/types/visitor_with_email.py new file mode 100644 index 00000000..5fca3548 --- /dev/null +++ b/src/intercom/visitors/types/visitor_with_email.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class VisitorWithEmail(UncheckedBaseModel): + email: str = pydantic.Field() + """ + The visitor's email. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/visitors/types/visitor_with_id.py b/src/intercom/visitors/types/visitor_with_id.py new file mode 100644 index 00000000..69449494 --- /dev/null +++ b/src/intercom/visitors/types/visitor_with_id.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class VisitorWithId(UncheckedBaseModel): + id: str = pydantic.Field() + """ + The unique identifier for the contact which is given by Intercom. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/intercom/visitors/types/visitor_with_user_id.py b/src/intercom/visitors/types/visitor_with_user_id.py new file mode 100644 index 00000000..658429b7 --- /dev/null +++ b/src/intercom/visitors/types/visitor_with_user_id.py @@ -0,0 +1,23 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2 +from ...core.unchecked_base_model import UncheckedBaseModel + + +class VisitorWithUserId(UncheckedBaseModel): + user_id: str = pydantic.Field() + """ + A unique identifier for the contact which is given to Intercom. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index 89ea997a..00000000 --- a/tests/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2012 keyes.ie -# -# License: http://jkeyes.mit-license.org/ -# diff --git a/tests/custom/test_client.py b/tests/custom/test_client.py new file mode 100644 index 00000000..ab04ce63 --- /dev/null +++ b/tests/custom/test_client.py @@ -0,0 +1,7 @@ +import pytest + + +# Get started with writing tests with pytest at https://docs.pytest.org +@pytest.mark.skip(reason="Unimplemented") +def test_client() -> None: + assert True diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py deleted file mode 100644 index c0bea9eb..00000000 --- a/tests/integration/__init__.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- coding: utf-8 -*- - -import time - -from datetime import datetime -from intercom import Company -from intercom import ResourceNotFound -from intercom import User - - -def get_timestamp(): - now = datetime.utcnow() - return int(time.mktime(now.timetuple())) - - -def get_or_create_user(timestamp): - # get user - email = '%s@example.com' % (timestamp) - try: - user = User.find(email=email) - except ResourceNotFound: - # Create a user - user = User.create( - email=email, - user_id=timestamp, - name="Ada %s" % (timestamp)) - time.sleep(5) - return user - - -def get_or_create_company(timestamp): - name = 'Company %s' % (timestamp) - - # get company - try: - company = Company.find(name=name) - except ResourceNotFound: - # Create a company - company = Company.create( - company_id=timestamp, name=name) - return company - - -def delete(resource): - try: - resource.delete() - except ResourceNotFound: - # not much we can do here - pass diff --git a/tests/integration/issues/__init__.py b/tests/integration/issues/__init__.py deleted file mode 100644 index 40a96afc..00000000 --- a/tests/integration/issues/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/tests/integration/issues/test_72.py b/tests/integration/issues/test_72.py deleted file mode 100644 index c576cae0..00000000 --- a/tests/integration/issues/test_72.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- - -import os -import unittest -import time -from intercom import Intercom -from intercom import Event -from intercom import User - -Intercom.app_id = os.environ.get('INTERCOM_APP_ID') -Intercom.app_api_key = os.environ.get('INTERCOM_APP_API_KEY') - - -class Issue72Test(unittest.TestCase): - - def test(self): - User.create(email='me@example.com') - # no exception here as empty response expected - data = { - 'event_name': 'Eventful 1', - 'created_at': int(time.time()), - 'email': 'me@example.com' - } - Event.create(**data) diff --git a/tests/integration/issues/test_73.py b/tests/integration/issues/test_73.py deleted file mode 100644 index cc5ce90c..00000000 --- a/tests/integration/issues/test_73.py +++ /dev/null @@ -1,34 +0,0 @@ -# -*- coding: utf-8 -*- -""" -How do I record when a User has started a new session? -""" - -import os -import unittest -from intercom import Intercom -from intercom import User - -Intercom.app_id = os.environ.get('INTERCOM_APP_ID') -Intercom.app_api_key = os.environ.get('INTERCOM_APP_API_KEY') - - -class Issue73Test(unittest.TestCase): - - def test(self): - user = User.create(email='bingo@example.com') - # store current session count - session_count = user.session_count - - # register a new session - user.new_session = True - user.save() - - # count has increased by 1 - self.assertEquals(session_count + 1, user.session_count) - - # register a new session - user.new_session = True - user.save() - - # count has increased by 1 - self.assertEquals(session_count + 2, user.session_count) diff --git a/tests/integration/test_admin.py b/tests/integration/test_admin.py deleted file mode 100644 index 22d59381..00000000 --- a/tests/integration/test_admin.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- - -import os -import unittest -from intercom import Intercom -from intercom import Admin - -Intercom.app_id = os.environ.get('INTERCOM_APP_ID') -Intercom.app_api_key = os.environ.get('INTERCOM_APP_API_KEY') - - -class AdminTest(unittest.TestCase): - - def test(self): - # Iterate over all admins - for admin in Admin.all(): - self.assertIsNotNone(admin.id) - self.assertIsNotNone(admin.email) diff --git a/tests/integration/test_company.py b/tests/integration/test_company.py deleted file mode 100644 index e7e285b1..00000000 --- a/tests/integration/test_company.py +++ /dev/null @@ -1,97 +0,0 @@ -# -*- coding: utf-8 -*- - -import os -import unittest -from intercom import Company -from intercom import Intercom -from intercom import User -from . import delete -from . import get_or_create_user -from . import get_or_create_company -from . import get_timestamp - -Intercom.app_id = os.environ.get('INTERCOM_APP_ID') -Intercom.app_api_key = os.environ.get('INTERCOM_APP_API_KEY') - - -class CompanyTest(unittest.TestCase): - - @classmethod - def setup_class(cls): - nowstamp = get_timestamp() - cls.company = get_or_create_company(nowstamp) - cls.user = get_or_create_user(nowstamp) - - @classmethod - def teardown_class(cls): - delete(cls.company) - delete(cls.user) - - def test_add_user(self): - user = User.find(email=self.user.email) - user.companies = [ - {"company_id": 6, "name": "Intercom"}, - {"company_id": 9, "name": "Test Company"} - ] - user.save() - user = User.find(email=self.user.email) - self.assertEqual(len(user.companies), 2) - self.assertEqual(user.companies[0].company_id, "9") - - def test_add_user_custom_attributes(self): - user = User.find(email=self.user.email) - user.companies = [ - { - "id": 6, - "name": "Intercom", - "custom_attributes": { - "referral_source": "Google" - } - } - ] - user.save() - user = User.find(email=self.user.email) - self.assertEqual(len(user.companies), 2) - self.assertEqual(user.companies[0].company_id, "9") - - # check the custom attributes - company = Company.find(company_id=6) - self.assertEqual( - company.custom_attributes['referral_source'], "Google") - - def test_find_by_company_id(self): - # Find a company by company_id - company = Company.find(company_id=self.company.company_id) - self.assertEqual(company.company_id, self.company.company_id) - - def test_find_by_company_name(self): - # Find a company by name - company = Company.find(name=self.company.name) - self.assertEqual(company.name, self.company.name) - - def test_find_by_id(self): - # Find a company by _id - company = Company.find(id=self.company.id) - self.assertEqual(company.company_id, self.company.company_id) - - def test_update(self): - # Find a company by id - company = Company.find(id=self.company.id) - # Update a company - now = get_timestamp() - updated_name = 'Company %s' % (now) - company.name = updated_name - company.save() - company = Company.find(id=self.company.id) - self.assertEqual(company.name, updated_name) - - def test_iterate(self): - # Iterate over all companies - for company in Company.all(): - self.assertTrue(company.id is not None) - - def test_users(self): - company = Company.find(id=self.company.id) - # Get a list of users in a company - for user in company.users: - self.assertIsNotNone(user.email) diff --git a/tests/integration/test_conversations.py b/tests/integration/test_conversations.py deleted file mode 100644 index 0a5655cb..00000000 --- a/tests/integration/test_conversations.py +++ /dev/null @@ -1,167 +0,0 @@ -# -*- coding: utf-8 -*- - -import os -import unittest -from intercom import Intercom -from intercom import Admin -from intercom import Conversation -from intercom import Message -from . import delete -from . import get_or_create_user -from . import get_timestamp - -Intercom.app_id = os.environ.get('INTERCOM_APP_ID') -Intercom.app_api_key = os.environ.get('INTERCOM_APP_API_KEY') - - -class ConversationTest(unittest.TestCase): - @classmethod - def setup_class(cls): - # get admin - cls.admin = Admin.all()[1] - - # get user - timestamp = get_timestamp() - cls.user = get_or_create_user(timestamp) - cls.email = cls.user.email - - # send user message - message_data = { - 'from': { - 'type': "user", - 'id': cls.user.id - }, - 'body': "Hey" - } - cls.user_message = Message.create(**message_data) - - conversations = Conversation.find_all() - user_init_conv = conversations[0] - # send admin reply - cls.admin_conv = user_init_conv.reply( - type='admin', admin_id=cls.admin.id, - message_type='comment', body='There') - - @classmethod - def teardown_class(cls): - delete(cls.user) - - def test_find_all_admin(self): - # FINDING CONVERSATIONS FOR AN ADMIN - # Iterate over all conversations (open and closed) assigned to an admin - for convo in Conversation.find_all(type='admin', id=self.admin.id): - self.assertIsNotNone(convo.id) - self.admin_conv.id = convo.id - - def test_find_all_open_admin(self): - # Iterate over all open conversations assigned to an admin - for convo in Conversation.find_all( - type='admin', id=self.admin.id, open=True): - self.assertIsNotNone(convo.id) - - def test_find_all_closed_admin(self): - # Iterate over closed conversations assigned to an admin - for convo in Conversation.find_all( - type='admin', id=self.admin.id, open=False): - self.assertIsNotNone(convo.id) - - def test_find_all_closed_before_admin(self): - for convo in Conversation.find_all( - type='admin', id=self.admin.id, open=False, - before=1374844930): - self.assertIsNotNone(convo.id) - - def test_find_all_user(self): - # FINDING CONVERSATIONS FOR A USER - # Iterate over all conversations (read + unread, correct) with a - # user based on the users email - for convo in Conversation.find_all(email=self.email, type='user'): - self.assertIsNotNone(convo.id) - - def test_find_all_read(self): - # Iterate over through all conversations (read + unread) with a - # user based on the users email - for convo in Conversation.find_all( - email=self.email, type='user', unread=False): - self.assertIsNotNone(convo.id) - - def test_find_all_unread(self): - # Iterate over all unread conversations with a user based on the - # users email - for convo in Conversation.find_all( - email=self.email, type='user', unread=True): - self.assertIsNotNone(convo.id) - - def test_find_single_conversation(self): - # FINDING A SINGLE CONVERSATION - convo_id = Conversation.find_all(type='admin', id=self.admin.id)[0].id - conversation = Conversation.find(id=convo_id) - self.assertEqual(conversation.id, convo_id) - - def test_conversation_parts(self): - # INTERACTING WITH THE PARTS OF A CONVERSATION - convo_id = Conversation.find_all(type='admin', id=self.admin.id)[0].id - conversation = Conversation.find(id=convo_id) - - # Getting the subject of a part (only applies to email-based - # conversations) - self.assertEqual(conversation.conversation_message.subject, "") - for part in conversation.conversation_parts: - # There is a part_type - self.assertIsNotNone(part.part_type) - # There is a body - if not part.part_type == 'assignment': - self.assertIsNotNone(part.body) - - def test_reply(self): - # REPLYING TO CONVERSATIONS - conversation = Conversation.find(id=self.admin_conv.id) - num_parts = len(conversation.conversation_parts) - # User (identified by email) replies with a comment - conversation.reply( - type='user', email=self.email, - message_type='comment', body='foo') - # Admin (identified by admin_id) replies with a comment - conversation.reply( - type='admin', admin_id=self.admin.id, - message_type='comment', body='bar') - conversation = Conversation.find(id=self.admin_conv.id) - self.assertEqual(num_parts + 2, len(conversation.conversation_parts)) - - def test_open(self): - # OPENING CONVERSATIONS - conversation = Conversation.find(id=self.admin_conv.id) - conversation.close_conversation(admin_id=self.admin.id, body='Closing message') - self.assertFalse(conversation.open) - conversation.open_conversation(admin_id=self.admin.id, body='Opening message') - conversation = Conversation.find(id=self.admin_conv.id) - self.assertTrue(conversation.open) - - def test_close(self): - # CLOSING CONVERSATIONS - conversation = Conversation.find(id=self.admin_conv.id) - self.assertTrue(conversation.open) - conversation.close_conversation(admin_id=self.admin.id, body='Closing message') - conversation = Conversation.find(id=self.admin_conv.id) - self.assertFalse(conversation.open) - - def test_assignment(self): - # ASSIGNING CONVERSATIONS - conversation = Conversation.find(id=self.admin_conv.id) - num_parts = len(conversation.conversation_parts) - conversation.assign(assignee_id=self.admin.id, admin_id=self.admin.id) - conversation = Conversation.find(id=self.admin_conv.id) - self.assertEqual(num_parts + 1, len(conversation.conversation_parts)) - self.assertEqual("assignment", conversation.conversation_parts[-1].part_type) - - def test_mark_read(self): - # MARKING A CONVERSATION AS READ - conversation = Conversation.find(id=self.admin_conv.id) - conversation.read = False - conversation.save() - conversation = Conversation.find(id=self.admin_conv.id) - self.assertFalse(conversation.read) - conversation.read = True - conversation.save() - conversation = Conversation.find(id=self.admin_conv.id) - self.assertTrue(conversation.read) diff --git a/tests/integration/test_count.py b/tests/integration/test_count.py deleted file mode 100644 index 806c33cd..00000000 --- a/tests/integration/test_count.py +++ /dev/null @@ -1,85 +0,0 @@ -# -*- coding: utf-8 -*- - -import os -import unittest -from intercom import Intercom -from intercom import Company -from intercom import Count -from intercom import Segment -from intercom import Tag -from intercom import User -from nose.tools import eq_ -from nose.tools import ok_ -from . import get_timestamp -from . import get_or_create_company -from . import get_or_create_user -from . import delete - -Intercom.app_id = os.environ.get('INTERCOM_APP_ID') -Intercom.app_api_key = os.environ.get('INTERCOM_APP_API_KEY') - - -class CountTest(unittest.TestCase): - - @classmethod - def setup_class(cls): - nowstamp = get_timestamp() - cls.company = get_or_create_company(nowstamp) - cls.user = get_or_create_user(nowstamp) - - @classmethod - def teardown_class(cls): - delete(cls.company) - delete(cls.user) - print(Intercom.rate_limit_details) - - def test_user_counts_for_each_tag(self): - # Get User Tag Count Object - Tag.tag_users('blue', [self.user.id]) - counts = Count.user_counts_for_each_tag - Tag.untag_users('blue', [self.user.id]) - for count in counts: - if 'blue' in count: - eq_(count['blue'], 1) - - def test_user_counts_for_each_segment(self): - # Get User Segment Count Object - counts = Count.user_counts_for_each_segment - ok_(counts) - - def test_company_counts_for_each_segment(self): - # Get Company Segment Count Object - counts = Count.company_counts_for_each_segment - ok_(counts) - - def test_company_counts_for_each_tag(self): - # Get Company Tag Count Object - Tag.tag_companies('blue', [self.company.id]) - counts = Count.company_counts_for_each_tag - Tag.untag_companies('blue', [self.company.id]) - # for count in counts: - # if 'blue' in count: - # eq_(count['blue'], 1) - - def test_company_counts_for_each_user(self): - # Get Company User Count Object - self.user.companies = [ - {"company_id": self.company.company_id} - ] - self.user.save() - counts = Count.company_counts_for_each_user - for count in counts: - if self.company.name in count: - eq_(count[self.company.name], 1) - - def test_total_company_count(self): - ok_(Company.count() >= 0) - - def test_total_user_count(self): - ok_(User.count() >= 0) - - def test_total_segment_count(self): - ok_(Segment.count() >= 0) - - def test_total_tag_count(self): - ok_(Tag.count() >= 0) diff --git a/tests/integration/test_notes.py b/tests/integration/test_notes.py deleted file mode 100644 index eba0a03b..00000000 --- a/tests/integration/test_notes.py +++ /dev/null @@ -1,58 +0,0 @@ -# -*- coding: utf-8 -*- - -import os -import unittest -from intercom import Intercom -from intercom import Note -from . import delete -from . import get_or_create_user -from . import get_timestamp - -Intercom.app_id = os.environ.get('INTERCOM_APP_ID') -Intercom.app_api_key = os.environ.get('INTERCOM_APP_API_KEY') - - -class NoteTest(unittest.TestCase): - @classmethod - def setup_class(cls): - timestamp = get_timestamp() - cls.user = get_or_create_user(timestamp) - cls.email = cls.user.email - - @classmethod - def teardown_class(cls): - delete(cls.user) - - def test_create_note(self): - # Create a note for a user - note = Note.create( - body="

Text for the note

", - email=self.email) - self.assertIsNotNone(note.id) - - def test_find_note(self): - # Find a note by id - orig_note = Note.create( - body="

Text for the note

", - email=self.email) - note = Note.find(id=orig_note.id) - self.assertEqual(note.body, orig_note.body) - - def test_find_all_email(self): - # Iterate over all notes for a user via their email address - notes = Note.find_all(email=self.email) - for note in notes: - self.assertTrue(note.id is not None) - user = note.user.load() - self.assertEqual(user.email, self.email) - break - - def test_find_all_id(self): - from intercom.user import User - user = User.find(email=self.email) - - # Iterate over all notes for a user via their email address - for note in Note.find_all(user_id=user.user_id): - self.assertTrue(note.id is not None) - user = note.user.load() - self.assertEqual(user.email, self.email) diff --git a/tests/integration/test_segments.py b/tests/integration/test_segments.py deleted file mode 100644 index d9b54f80..00000000 --- a/tests/integration/test_segments.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- - -import os -import time -import unittest -from datetime import datetime -from intercom import Intercom -from intercom import Segment - -Intercom.app_id = os.environ.get('INTERCOM_APP_ID') -Intercom.app_api_key = os.environ.get('INTERCOM_APP_API_KEY') - - -class SegmentTest(unittest.TestCase): - - @classmethod - def setup_class(cls): - cls.segment = Segment.all()[0] - - def test_find_segment(self): - # Find a segment - segment = Segment.find(id=self.segment.id) - self.assertEqual(segment.id, self.segment.id) - - def test_save_segment(self): - # Update a segment - segment = Segment.find(id=self.segment.id) - now = datetime.utcnow() - updated_name = 'Updated %s' % (time.mktime(now.timetuple())) - segment.name = updated_name - segment.save() - segment = Segment.find(id=self.segment.id) - self.assertEqual(segment.name, updated_name) - - def test_iterate(self): - # Iterate over all segments - for segment in Segment.all(): - self.assertTrue(segment.id is not None) diff --git a/tests/integration/test_tags.py b/tests/integration/test_tags.py deleted file mode 100644 index cf7579a5..00000000 --- a/tests/integration/test_tags.py +++ /dev/null @@ -1,101 +0,0 @@ -# -*- coding: utf-8 -*- - -import os -import unittest -from intercom import Intercom -from intercom import Tag -from intercom import User -from intercom import Company -from . import delete -from . import get_or_create_company -from . import get_or_create_user -from . import get_timestamp - -Intercom.app_id = os.environ.get('INTERCOM_APP_ID') -Intercom.app_api_key = os.environ.get('INTERCOM_APP_API_KEY') - - -class TagTest(unittest.TestCase): - - @classmethod - def setup_class(cls): - nowstamp = get_timestamp() - cls.company = get_or_create_company(nowstamp) - cls.user = get_or_create_user(nowstamp) - cls.user.companies = [ - {"company_id": cls.company.id, "name": cls.company.name} - ] - cls.user.save() - - @classmethod - def teardown_class(cls): - delete(cls.company) - delete(cls.user) - - def test_tag_users(self): - # Tag users - tag = Tag.tag_users('blue', [self.user.id]) - self.assertEqual(tag.name, 'blue') - user = User.find(email=self.user.email) - self.assertEqual(1, len(user.tags)) - - def test_untag_users(self): - # Untag users - tag = Tag.untag_users('blue', [self.user.id]) - self.assertEqual(tag.name, 'blue') - user = User.find(email=self.user.email) - self.assertEqual(0, len(user.tags)) - - def test_all(self): - # Iterate over all tags - for tag in Tag.all(): - self.assertIsNotNone(tag.id) - - def test_all_for_user_by_id(self): - # Iterate over all tags for user - tags = Tag.find_all_for_user(id=self.user.id) - for tag in tags: - self.assertIsNotNone(tag.id) - - def test_all_for_user_by_email(self): - # Iterate over all tags for user - tags = Tag.find_all_for_user(email=self.user.email) - for tag in tags: - self.assertIsNotNone(tag.id) - - def test_all_for_user_by_user_id(self): - # Iterate over all tags for user - tags = Tag.find_all_for_user(user_id=self.user.user_id) - for tag in tags: - self.assertIsNotNone(tag.id) - - def test_tag_companies(self): - # Tag companies - tag = Tag.tag_companies("red", [self.user.companies[0].id]) - self.assertEqual(tag.name, "red") - company = Company.find(id=self.user.companies[0].id) - self.assertEqual(1, len(company.tags)) - - def test_untag_companies(self): - # Untag companies - tag = Tag.untag_companies("red", [self.user.companies[0].id]) - self.assertEqual(tag.name, "red") - company = Company.find(id=self.user.companies[0].id) - self.assertEqual(0, len(company.tags)) - - # Iterate over all tags for company - def test_all_for_company_by_id(self): - # Iterate over all tags for user - red_tag = Tag.tag_companies("red", [self.company.id]) - tags = Tag.find_all_for_company(id=self.company.id) - for tag in tags: - self.assertEqual(red_tag.id, tag.id) - Tag.untag_companies("red", [self.company.id]) - - def test_all_for_company_by_company_id(self): - # Iterate over all tags for user - red_tag = Tag.tag_companies("red", [self.company.id]) - tags = Tag.find_all_for_company(company_id=self.company.id) - for tag in tags: - self.assertEqual(red_tag.id, tag.id) - Tag.untag_companies("red", [self.company.id]) diff --git a/tests/integration/test_user.py b/tests/integration/test_user.py deleted file mode 100644 index 73fcd9f2..00000000 --- a/tests/integration/test_user.py +++ /dev/null @@ -1,65 +0,0 @@ -# -*- coding: utf-8 -*- - -import os -import unittest -from intercom import Intercom -from intercom import User -from . import get_timestamp -from . import get_or_create_user -from . import delete - -Intercom.app_id = os.environ.get('INTERCOM_APP_ID') -Intercom.app_api_key = os.environ.get('INTERCOM_APP_API_KEY') - - -class UserTest(unittest.TestCase): - - @classmethod - def setup_class(cls): - nowstamp = get_timestamp() - cls.user = get_or_create_user(nowstamp) - cls.email = cls.user.email - - @classmethod - def teardown_class(cls): - delete(cls.user) - - def test_find_by_email(self): - # Find user by email - user = User.find(email=self.email) - self.assertEqual(self.email, user.email) - - def test_find_by_user_id(self): - # Find user by user id - user = User.find(user_id=self.user.user_id) - self.assertEqual(self.email, user.email) - - def test_find_by_id(self): - # Find user by id - user = User.find(id=self.user.id) - self.assertEqual(self.email, user.email) - - def test_custom_attributes(self): - # Update custom_attributes for a user - user = User.find(id=self.user.id) - user.custom_attributes["average_monthly_spend"] = 1234.56 - user.save() - user = User.find(id=self.user.id) - self.assertEqual( - user.custom_attributes["average_monthly_spend"], 1234.56) - - def test_increment(self): - # Perform incrementing - user = User.find(id=self.user.id) - karma = user.custom_attributes.get('karma', 0) - user.increment('karma') - user.save() - self.assertEqual(user.custom_attributes["karma"], karma + 1) - user.increment('karma') - user.save() - self.assertEqual(user.custom_attributes["karma"], karma + 2) - - def test_iterate(self): - # Iterate over all users - for user in User.all(): - self.assertTrue(user.id is not None) diff --git a/tests/run_tests.sh b/tests/run_tests.sh deleted file mode 100755 index aec327f3..00000000 --- a/tests/run_tests.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -nosetests tests/unit -exit $? diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py deleted file mode 100644 index c8669ffa..00000000 --- a/tests/unit/__init__.py +++ /dev/null @@ -1,298 +0,0 @@ -# -*- coding: utf-8 -*- - -import json -import os - -from mock import Mock - -DIRPATH = os.path.dirname(__file__) -FIXTURES = os.path.join(DIRPATH, 'fixtures') - - -def create_response(status, fixture=None): - def request(*args, **kwargs): - response = Mock() - response.status_code = status - if fixture: - fixture_path = os.path.join(FIXTURES, fixture) - response.content = open(fixture_path).read() - return response - return request - - -def local_response(**params): - def _call(*args, **kwargs): - response = Mock() - reply = {} - for name, value in list(kwargs.items()): - reply[name] = value - for name, value in list(params.items()): - reply[name] = value - response.content = json.dumps(reply) - response.status_code = 200 - return response - return _call - - -def mock_response(content, status_code=200, encoding='utf-8', headers=None): - if headers is None: - headers = { - 'x-ratelimit-limit': 500, - 'x-ratelimit-remaining': 500, - 'x-ratelimit-reset': 1427932858 - } - return Mock( - content=content, status_code=status_code, encoding=encoding, headers=headers) - - -def get_user(email="bob@example.com", name="Joe Schmoe"): - return { - "type": "user", - "id": "aaaaaaaaaaaaaaaaaaaaaaaa", - "user_id": 'id-from-customers-app', - "email": email, - "name": name, - "avatar": { - "type": "avatar", - "image_url": "https://graph.facebook.com/1/picture?width=24&height=24" - }, - "app_id": "the-app-id", - "created_at": 1323422442, - "custom_attributes": {"a": "b", "b": 2}, - "companies": { - "type": "company.list", - "companies": [ - { - "type": "company", - "company_id": "123", - "id": "bbbbbbbbbbbbbbbbbbbbbbbb", - "app_id": "the-app-id", - "name": "Company 1", - "remote_created_at": 1390936440, - "created_at": 1401970114, - "updated_at": 1401970114, - "last_request_at": 1401970113, - "monthly_spend": 0, - "session_count": 0, - "user_count": 1, - "tag_ids": [], - "custom_attributes": { - "category": "Tech" - } - } - ] - }, - "session_count": 123, - "unsubscribed_from_emails": True, - "last_request_at": 1401970113, - "created_at": 1401970114, - "remote_created_at": 1393613864, - "updated_at": 1401970114, - "user_agent_data": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11", - "social_profiles": { - "type": "social_profile.list", - "social_profiles": [ - { - "type": "social_profile", - "name": "twitter", - "url": "http://twitter.com/abc", - "username": "abc", - "id": None - }, - { - "type": "social_profile", - "name": "twitter", - "username": "abc2", - "url": "http://twitter.com/abc2", - "id": None - }, - { - "type": "social_profile", - "name": "facebook", - "url": "http://facebook.com/abc", - "username": "abc", - "id": "1234242" - }, - { - "type": "social_profile", - "name": "quora", - "url": "http://facebook.com/abc", - "username": "abc", - "id": "1234242" - } - ] - }, - "location_data": { - "type": "location_data", - "city_name": 'Dublin', - "continent_code": 'EU', - "country_name": 'Ireland', - "latitude": '90', - "longitude": '10', - "postal_code": 'IE', - "region_name": 'Europe', - "timezone": '+1000', - "country_code": "IRL" - } - } - - -def page_of_users(include_next_link=False): - page = { - "type": "user.list", - "pages": { - "type": "pages", - "page": 1, - "next": None, - "per_page": 50, - "total_pages": 7 - }, - "users": [ - get_user("user1@example.com"), - get_user("user2@example.com"), - get_user("user3@example.com")], - "total_count": 314 - } - if include_next_link: - page["pages"]["next"] = "https://api.intercom.io/users?per_page=50&page=2" - return page - -test_tag = { - "id": "4f73428b5e4dfc000b000112", - "name": "Test Tag", - "segment": False, - "tagged_user_count": 2 -} - -test_subscription = { - "type": "notification_subscription", - "id": "nsub_123456789", - "created_at": 1410368642, - "updated_at": 1410368642, - "service_type": "web", - "app_id": "3qmk5gyg", - "url": "http://example.com", - "self": "https://api.intercom.io/subscriptions/nsub_123456789", - "topics": ["user.created", "conversation.user.replied", "conversation.admin.replied"], - "active": True, - "metadata": {}, - "hub_secret": None, - "mode": "point", - "links": { - "sent": "https://api.intercom.io/subscriptions/nsub_123456789/sent", - "retry": "https://api.intercom.io/subscriptions/nsub_123456789/retry", - "errors": "https://api.intercom.io/subscriptions/nsub_123456789/errors" - }, - "notes": [] -} - -test_user_notification = { - "type": "notification_event", - "id": "notif_123456-56465-546546", - "topic": "user.created", - "app_id": "aaaaaa", - "data": { - "type": "notification_event_data", - "item": { - "type": "user", - "id": "aaaaaaaaaaaaaaaaaaaaaaaa", - "user_id": None, - "email": "joe@example.com", - "name": "Joe Schmoe", - "avatar": { - "type": "avatar", - "image_url": None - }, - "app_id": "aaaaa", - "companies": { - "type": "company.list", - "companies": [] - }, - "location_data": { - }, - "last_request_at": None, - "created_at": "1401970114", - "remote_created_at": None, - "updated_at": "1401970114", - "session_count": 0, - "social_profiles": { - "type": "social_profile.list", - "social_profiles": [] - }, - "unsubscribed_from_emails": False, - "user_agent_data": None, - "tags": { - "type": "tag.list", - "tags": [] - }, - "segments": { - "type": "segment.list", - "segments": [] - }, - "custom_attributes": { - } - } - }, - "delivery_status": None, - "delivery_attempts": 1, - "delivered_at": 0, - "first_sent_at": 1410188629, - "created_at": 1410188628, - "links": {}, - "self": None -} - -test_conversation_notification = { - "type": "notification_event", - "id": "notif_123456-56465-546546", - "topic": "conversation.user.created", - "app_id": "aaaaa", - "data": { - "type": "notification_event_data", - "item": { - "type": "conversation", - "id": "123456789", - "created_at": "1410335293", - "updated_at": "1410335293", - "user": { - "type": "user", - "id": "540f1de7112d3d1d51001637", - "name": "Kill Bill", - "email": "bill@bill.bill" - }, - "assignee": { - "type": "nobody_admin", - "id": None - }, - "conversation_message": { - "type": "conversation_message", - "id": "321546", - "subject": "", - "body": "

An important message

", - "author": { - "type": "user", - "id": "aaaaaaaaaaaaaaaaaaaaaa", - "name": "Kill Bill", - "email": "bill@bill.bill" - }, - "attachments": [] - }, - "conversation_parts": { - "type": "conversation_part.list", - "conversation_parts": [] - }, - "open": None, - "read": True, - "links": { - "conversation_web": "https://app.intercom.io/a/apps/aaaaaa/inbox/all/conversations/123456789" - } - } - }, - "delivery_status": None, - "delivery_attempts": 1, - "delivered_at": 0, - "first_sent_at": 1410335293, - "created_at": 1410335293, - "links": {}, - "self": "http://example.com/resource/url/" -} diff --git a/tests/unit/lib/__init__.py b/tests/unit/lib/__init__.py deleted file mode 100644 index 40a96afc..00000000 --- a/tests/unit/lib/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/tests/unit/lib/test_flat_store.py b/tests/unit/lib/test_flat_store.py deleted file mode 100644 index c91d8e77..00000000 --- a/tests/unit/lib/test_flat_store.py +++ /dev/null @@ -1,43 +0,0 @@ -# -*- coding: utf-8 -*- - -import unittest -from intercom.lib.flat_store import FlatStore -from nose.tools import assert_raises -from nose.tools import eq_ -from nose.tools import istest - - -class IntercomFlatStore(unittest.TestCase): - - @istest - def it_raises_if_you_try_to_set_or_merge_in_nested_hash_structures(self): - data = FlatStore() - with assert_raises(ValueError): - data["thing"] = [1] - with assert_raises(ValueError): - data["thing"] = {1: 2} - with assert_raises(ValueError): - FlatStore(**{"1": {2: 3}}) - - @istest - def it_raises_if_you_try_to_use_a_non_string_key(self): - data = FlatStore() - with assert_raises(ValueError): - data[1] = "something" - - @istest - def it_sets_and_merges_valid_entries(self): - data = FlatStore() - data["a"] = 1 - data["b"] = 2 - eq_(data["a"], 1) - eq_(data["b"], 2) - data = FlatStore(a=1, b=2) - eq_(data["a"], 1) - eq_(data["b"], 2) - - @istest - def it_sets_null_entries(self): - data = FlatStore() - data["a"] = None - eq_(data["a"], None) diff --git a/tests/unit/test_admin.py b/tests/unit/test_admin.py deleted file mode 100644 index a22e550b..00000000 --- a/tests/unit/test_admin.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- - -import unittest - -from intercom import Request -from intercom import Admin -from intercom.collection_proxy import CollectionProxy -from mock import patch -from nose.tools import assert_raises -from nose.tools import istest - - -def send_request(*args, **kwargs): - # empty impl - raise (AssertionError) - - -class AdminTest(unittest.TestCase): - - @istest - @patch.object(Request, 'send_request_to_path', send_request) - def it_returns_a_collection_proxy_for_all_without_making_any_requests(self): # noqa - # prove a call to send_request_to_path will raise an error - with assert_raises(AssertionError): - send_request() - all = Admin.all() - self.assertIsInstance(all, CollectionProxy) diff --git a/tests/unit/test_collection_proxy.py b/tests/unit/test_collection_proxy.py deleted file mode 100644 index cbc33dfc..00000000 --- a/tests/unit/test_collection_proxy.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- - -import unittest - -from intercom import Intercom -from intercom import User -from mock import call -from mock import patch -from nose.tools import eq_ -from nose.tools import istest -from tests.unit import page_of_users - - -class CollectionProxyTest(unittest.TestCase): - - @istest - def it_stops_iterating_if_no_next_link(self): - body = page_of_users(include_next_link=False) - with patch.object(Intercom, 'get', return_value=body) as mock_method: - emails = [user.email for user in User.all()] - mock_method.assert_called_once_with('/users') - eq_(emails, ['user1@example.com', 'user2@example.com', 'user3@example.com']) # noqa - - @istest - def it_keeps_iterating_if_next_link(self): - page1 = page_of_users(include_next_link=True) - page2 = page_of_users(include_next_link=False) - side_effect = [page1, page2] - with patch.object(Intercom, 'get', side_effect=side_effect) as mock_method: # noqa - emails = [user.email for user in User.all()] - eq_([call('/users'), call('https://api.intercom.io/users?per_page=50&page=2')], # noqa - mock_method.mock_calls) - eq_(emails, ['user1@example.com', 'user2@example.com', 'user3@example.com'] * 2) # noqa - - @istest - def it_supports_indexed_array_access(self): - body = page_of_users(include_next_link=False) - with patch.object(Intercom, 'get', return_value=body) as mock_method: - eq_(User.all()[0].email, 'user1@example.com') - mock_method.assert_called_once_with('/users') - - @istest - def it_supports_querying(self): - body = page_of_users(include_next_link=False) - with patch.object(Intercom, 'get', return_value=body) as mock_method: - emails = [user.email for user in User.find_all(tag_name='Taggart J')] # noqa - eq_(emails, ['user1@example.com', 'user2@example.com', 'user3@example.com']) # noqa - mock_method.assert_called_once_with('/users', tag_name='Taggart J') diff --git a/tests/unit/test_company.py b/tests/unit/test_company.py deleted file mode 100644 index 3a406baf..00000000 --- a/tests/unit/test_company.py +++ /dev/null @@ -1,45 +0,0 @@ -# -*- coding: utf-8 -*- - -import intercom -import unittest - -from intercom import Company -from intercom import Intercom -from mock import call -from mock import patch -from nose.tools import assert_raises -from nose.tools import eq_ -from nose.tools import istest - - -class CompanyTest(unittest.TestCase): - - @istest - def it_raises_error_if_no_response_on_find(self): - with patch.object(Intercom, 'get', return_value=None) as mock_method: - with assert_raises(intercom.HttpError): - Company.find(company_id='4') - mock_method.assert_called_once_with('/companies', company_id='4') - - @istest - def it_raises_error_if_no_response_on_find_all(self): - with patch.object(Intercom, 'get', return_value=None) as mock_method: - with assert_raises(intercom.HttpError): - [x for x in Company.all()] - mock_method.assert_called_once_with('/companies') - - @istest - def it_raises_error_on_load(self): - data = { - 'type': 'user', - 'id': 'aaaaaaaaaaaaaaaaaaaaaaaa', - 'company_id': '4', - 'name': 'MyCo' - } - side_effect = [data, None] - with patch.object(Intercom, 'get', side_effect=side_effect) as mock_method: # noqa - company = Company.find(company_id='4') - with assert_raises(intercom.HttpError): - company.load() - eq_([call('/companies', company_id='4'), call('/companies/aaaaaaaaaaaaaaaaaaaaaaaa')], # noqa - mock_method.mock_calls) diff --git a/tests/unit/test_event.py b/tests/unit/test_event.py deleted file mode 100644 index 9068dbc2..00000000 --- a/tests/unit/test_event.py +++ /dev/null @@ -1,50 +0,0 @@ -# -*- coding: utf-8 -*- - -import time -import unittest - -from datetime import datetime -from intercom import User -from intercom import Intercom -from intercom import Event -from mock import patch -from nose.tools import istest - - -class EventTest(unittest.TestCase): - - def setUp(self): # noqa - now = time.mktime(datetime.utcnow().timetuple()) - self.user = User( - email="jim@example.com", - user_id="12345", - created_at=now, - name="Jim Bob") - self.created_time = now - 300 - - @istest - def it_creates_an_event_with_metadata(self): - data = { - 'event_name': 'Eventful 1', - 'created_at': self.created_time, - 'email': 'joe@example.com', - 'metadata': { - 'invitee_email': 'pi@example.com', - 'invite_code': 'ADDAFRIEND', - 'found_date': 12909364407 - } - } - - with patch.object(Intercom, 'post', return_value=data) as mock_method: - Event.create(**data) - mock_method.assert_called_once_with('/events/', **data) - - @istest - def it_creates_an_event_without_metadata(self): - data = { - 'event_name': 'sale of item', - 'email': 'joe@example.com', - } - with patch.object(Intercom, 'post', return_value=data) as mock_method: - Event.create(**data) - mock_method.assert_called_once_with('/events/', **data) diff --git a/tests/unit/test_import.py b/tests/unit/test_import.py deleted file mode 100644 index a18287a9..00000000 --- a/tests/unit/test_import.py +++ /dev/null @@ -1,9 +0,0 @@ -# coding=utf-8 -# -# License: http://jkeyes.mit-license.org/ -# -from intercom import * - - -def test_wildcard_import(): - pass diff --git a/tests/unit/test_intercom.py b/tests/unit/test_intercom.py deleted file mode 100644 index b6534d82..00000000 --- a/tests/unit/test_intercom.py +++ /dev/null @@ -1,88 +0,0 @@ -# -*- coding: utf-8 -*- - -import intercom -import mock -import time -import unittest - -from datetime import datetime -from nose.tools import assert_raises -from nose.tools import eq_ -from nose.tools import istest - - -class ExpectingArgumentsTest(unittest.TestCase): - - def setUp(self): # noqa - self.intercom = intercom.Intercom - self.intercom.app_id = 'abc123' - self.intercom.app_api_key = 'super-secret-key' - - @istest - def it_raises_argumenterror_if_no_app_id_or_app_api_key_specified(self): # noqa - self.intercom.app_id = None - self.intercom.app_api_key = None - with assert_raises(intercom.ArgumentError): - self.intercom.target_base_url - - @istest - def it_returns_the_app_id_and_app_api_key_previously_set(self): - eq_(self.intercom.app_id, 'abc123') - eq_(self.intercom.app_api_key, 'super-secret-key') - - @istest - def it_defaults_to_https_to_api_intercom_io(self): - eq_(self.intercom.target_base_url, - 'https://abc123:super-secret-key@api.intercom.io') - - -class OverridingProtocolHostnameTest(unittest.TestCase): - def setUp(self): # noqa - self.intercom = intercom.Intercom - self.protocol = self.intercom.protocol - self.hostname = self.intercom.hostname - self.intercom.endpoints = None - - def tearDown(self): # noqa - self.intercom.protocol = self.protocol - self.intercom.hostname = self.hostname - self.intercom.endpoints = ["https://api.intercom.io"] - - @istest - def it_allows_overriding_of_the_endpoint_and_protocol(self): - self.intercom.protocol = "http" - self.intercom.hostname = "localhost:3000" - eq_( - self.intercom.target_base_url, - "http://abc123:super-secret-key@localhost:3000") - - @istest - def it_prefers_endpoints(self): - self.intercom.endpoint = "https://localhost:7654" - eq_(self.intercom.target_base_url, - "https://abc123:super-secret-key@localhost:7654") - - # turn off the shuffle - with mock.patch("random.shuffle") as mock_shuffle: - mock_shuffle.return_value = ["http://example.com", "https://localhost:7654"] # noqa - self.intercom.endpoints = ["http://example.com", "https://localhost:7654"] # noqa - eq_(self.intercom.target_base_url, - 'http://abc123:super-secret-key@example.com') - - @istest - def it_has_endpoints(self): - eq_(self.intercom.endpoints, ["https://api.intercom.io"]) - self.intercom.endpoints = ["http://example.com", "https://localhost:7654"] # noqa - eq_(self.intercom.endpoints, ["http://example.com", "https://localhost:7654"]) # noqa - - @istest - def it_should_randomize_endpoints_if_last_checked_endpoint_is_gt_5_minutes_ago(self): # noqa - now = time.mktime(datetime.utcnow().timetuple()) - self.intercom._endpoint_randomized_at = now - self.intercom.endpoints = ["http://alternative"] - self.intercom.current_endpoint = "http://start" - - self.intercom._endpoint_randomized_at = now - 120 - eq_(self.intercom.current_endpoint, "http://start") - self.intercom._endpoint_randomized_at = now - 360 - eq_(self.intercom.current_endpoint, "http://alternative") diff --git a/tests/unit/test_message.py b/tests/unit/test_message.py deleted file mode 100644 index 592292ac..00000000 --- a/tests/unit/test_message.py +++ /dev/null @@ -1,59 +0,0 @@ -# -*- coding: utf-8 -*- - -import time -import unittest - -from datetime import datetime -from intercom import Intercom -from intercom import User -from intercom import Message -from mock import patch -from nose.tools import eq_ -from nose.tools import istest - - -class MessageTest(unittest.TestCase): - - def setUp(self): # noqa - now = time.mktime(datetime.utcnow().timetuple()) - self.user = User( - email="jim@example.com", - user_id="12345", - created_at=now, - name="Jim Bob") - self.created_time = now - 300 - - @istest - def it_creates_a_user_message_with_string_keys(self): - data = { - 'from': { - 'type': 'user', - 'email': 'jim@example.com', - }, - 'body': 'halp' - } - with patch.object(Intercom, 'post', return_value=data) as mock_method: - message = Message.create(**data) - mock_method.assert_called_once_with('/messages/', **data) - eq_('halp', message.body) - - @istest - def it_creates_an_admin_message(self): - data = { - 'from': { - 'type': 'admin', - 'id': '1234', - }, - 'to': { - 'type': 'user', - 'id': '5678', - }, - 'body': 'halp', - 'message_type': 'inapp' - } - - with patch.object(Intercom, 'post', return_value=data) as mock_method: - message = Message.create(**data) - mock_method.assert_called_once_with('/messages/', **data) - eq_('halp', message.body) - eq_('inapp', message.message_type) diff --git a/tests/unit/test_note.py b/tests/unit/test_note.py deleted file mode 100644 index 7a9478e7..00000000 --- a/tests/unit/test_note.py +++ /dev/null @@ -1,42 +0,0 @@ -# -*- coding: utf-8 -*- - -import unittest - -from intercom import Intercom -from intercom import Note -from mock import patch -from nose.tools import eq_ -from nose.tools import istest - - -class NoteTest(unittest.TestCase): - - @istest - def it_creates_a_note(self): - data = { - 'body': '

Note to leave on user

', - 'created_at': 1234567890 - } - with patch.object(Intercom, 'post', return_value=data) as mock_method: - note = Note.create(body="Note to leave on user") - mock_method.assert_called_once_with('/notes/', body="Note to leave on user") # noqa - eq_(note.body, "

Note to leave on user

") - - @istest - def it_sets_gets_allowed_keys(self): - params = { - 'body': 'Note body', - 'email': 'me@example.com', - 'user_id': 'abc123' - } - params_keys = list(params.keys()) - params_keys.sort() - - note = Note(**params) - note_dict = note.to_dict - note_keys = list(note_dict.keys()) - note_keys.sort() - - eq_(params_keys, note_keys) - for key in params_keys: - eq_(getattr(note, key), params[key]) diff --git a/tests/unit/test_notification.py b/tests/unit/test_notification.py deleted file mode 100644 index 4c4c0f65..00000000 --- a/tests/unit/test_notification.py +++ /dev/null @@ -1,88 +0,0 @@ -# -*- coding: utf-8 -*- - -import unittest - -from intercom import Notification -from intercom.utils import create_class_instance -from nose.tools import eq_ -from nose.tools import istest -from tests.unit import test_conversation_notification -from tests.unit import test_user_notification - - -class NotificationTest(unittest.TestCase): - - @istest - def it_converts_notification_hash_to_object(self): - payload = Notification(**test_user_notification) - self.assertIsInstance(payload, Notification) - - @istest - def it_returns_correct_model_type_for_user(self): - payload = Notification(**test_user_notification) - User = create_class_instance('User') # noqa - - self.assertIsInstance(payload.model, User.__class__) - eq_(payload.model_type, User.__class__) - - @istest - def it_returns_correct_user_notification_topic(self): - payload = Notification(**test_user_notification) - eq_(payload.topic, "user.created") - - @istest - def it_returns_instance_of_user(self): - User = create_class_instance('User') # noqa - payload = Notification(**test_user_notification) - self.assertIsInstance(payload.model, User.__class__) - - @istest - def it_returns_instance_of_conversation(self): - Conversation = create_class_instance('Conversation') # noqa - payload = Notification(**test_conversation_notification) - self.assertIsInstance(payload.model, Conversation.__class__) - - @istest - def it_returns_correct_model_type_for_conversation(self): - Conversation = create_class_instance('Conversation') # noqa - payload = Notification(**test_conversation_notification) - eq_(payload.model_type, Conversation.__class__) - - @istest - def it_returns_correct_conversation_notification_topic(self): - payload = Notification(**test_conversation_notification) - eq_(payload.topic, "conversation.user.created") - - @istest - def it_returns_inner_user_object_for_conversation(self): - User = create_class_instance('User') # noqa - payload = Notification(**test_conversation_notification) - self.assertIsInstance(payload.model.user, User.__class__) - - @istest - def it_returns_inner_user_object_with_nil_tags(self): - user_notification = { - "type": "notification_event", - "app_id": "aa11aa", - "data": { - "type": "notification_event_data", - "item": { - "type": "user", - "id": "abc123def", - "user_id": "666", - "email": "joe@example.com", - "name": "Joe", - "tags": { - "type": "tag.list", - "tags": None - } - } - } - } - payload = Notification(**user_notification) - eq_(payload.model.tags, []) - - @istest - def it_has_self_attribute(self): - payload = Notification(**test_conversation_notification) - eq_('http://example.com/resource/url/', payload.self) diff --git a/tests/unit/test_request.py b/tests/unit/test_request.py deleted file mode 100644 index 79905cb1..00000000 --- a/tests/unit/test_request.py +++ /dev/null @@ -1,306 +0,0 @@ -# -*- coding: utf-8 -*- - -import intercom -import json -import unittest - -from intercom import Intercom -from intercom import Request -from intercom import UnexpectedError -from mock import Mock -from mock import patch -from nose.tools import assert_raises -from nose.tools import eq_ -from nose.tools import ok_ -from nose.tools import istest -from tests.unit import mock_response - - -class RequestTest(unittest.TestCase): - - @istest - def it_raises_resource_not_found(self): - resp = mock_response('{}', status_code=404) - with patch('requests.request') as mock_method: - mock_method.return_value = resp - with assert_raises(intercom.ResourceNotFound): - Request.send_request_to_path('GET', 'notes', ('x', 'y'), resp) - - @istest - def it_raises_authentication_error_unauthorized(self): - resp = mock_response('{}', status_code=401) - with patch('requests.request') as mock_method: - mock_method.return_value = resp - with assert_raises(intercom.AuthenticationError): - Request.send_request_to_path('GET', 'notes', ('x', 'y'), resp) - - @istest - def it_raises_authentication_error_forbidden(self): - resp = mock_response('{}', status_code=403) - with patch('requests.request') as mock_method: - mock_method.return_value = resp - with assert_raises(intercom.AuthenticationError): - Request.send_request_to_path('GET', 'notes', ('x', 'y'), resp) - - @istest - def it_raises_server_error(self): - resp = Mock(encoding="utf-8", content='{}', status_code=500) - with patch('requests.request') as mock_method: - mock_method.return_value = resp - with assert_raises(intercom.ServerError): - Request.send_request_to_path('GET', 'notes', ('x', 'y'), resp) - - @istest - def it_raises_bad_gateway_error(self): - resp = mock_response('{}', status_code=502) - with patch('requests.request') as mock_method: - mock_method.return_value = resp - with assert_raises(intercom.BadGatewayError): - Request.send_request_to_path('GET', 'notes', ('x', 'y'), resp) - - @istest - def it_raises_service_unavailable_error(self): - resp = mock_response('{}', status_code=503) - with patch('requests.request') as mock_method: - mock_method.return_value = resp - with assert_raises(intercom.ServiceUnavailableError): - Request.send_request_to_path('GET', 'notes', ('x', 'y'), resp) - - @istest - def it_raises_an_unexpected_typed_error(self): - payload = { - 'type': 'error.list', - 'errors': [ - { - 'type': 'hopper', - 'message': 'The first compiler.' - } - ] - } - content = json.dumps(payload).encode('utf-8') - resp = mock_response(content) - with patch('requests.request') as mock_method: - mock_method.return_value = resp - try: - Intercom.get('/users') - self.fail('UnexpectedError not raised.') - except (UnexpectedError) as err: - ok_("The error of type 'hopper' is not recognized" in err.message) # noqa - eq_(err.context['http_code'], 200) - eq_(err.context['application_error_code'], 'hopper') - - @istest - def it_raises_an_unexpected_untyped_error(self): - payload = { - 'type': 'error.list', - 'errors': [ - { - 'message': 'UNIVAC' - } - ] - } - content = json.dumps(payload).encode('utf-8') - resp = mock_response(content) - with patch('requests.request') as mock_method: - mock_method.return_value = resp - try: - Intercom.get('/users') - self.fail('UnexpectedError not raised.') - except (UnexpectedError) as err: - ok_("An unexpected error occured." in err.message) - eq_(err.context['application_error_code'], None) - - @istest - def it_raises_a_bad_request_error(self): - payload = { - 'type': 'error.list', - 'errors': [ - { - 'type': None, - 'message': 'email is required' - } - ] - } - - for code in ['missing_parameter', 'parameter_invalid', 'bad_request']: - payload['errors'][0]['type'] = code - - content = json.dumps(payload).encode('utf-8') - resp = mock_response(content) - with patch('requests.request') as mock_method: - mock_method.return_value = resp - with assert_raises(intercom.BadRequestError): - Intercom.get('/users') - - @istest - def it_raises_an_authentication_error(self): - payload = { - 'type': 'error.list', - 'errors': [ - { - 'type': 'unauthorized', - 'message': 'Your name\'s not down.' - } - ] - } - for code in ['unauthorized', 'forbidden']: - payload['errors'][0]['type'] = code - - content = json.dumps(payload).encode('utf-8') - resp = mock_response(content) - with patch('requests.request') as mock_method: - mock_method.return_value = resp - with assert_raises(intercom.AuthenticationError): - Intercom.get('/users') - - @istest - def it_raises_resource_not_found_by_type(self): - payload = { - 'type': 'error.list', - 'errors': [ - { - 'type': 'not_found', - 'message': 'Waaaaally?' - } - ] - } - content = json.dumps(payload).encode('utf-8') - resp = mock_response(content) - with patch('requests.request') as mock_method: - mock_method.return_value = resp - with assert_raises(intercom.ResourceNotFound): - Intercom.get('/users') - - @istest - def it_raises_rate_limit_exceeded(self): - payload = { - 'type': 'error.list', - 'errors': [ - { - 'type': 'rate_limit_exceeded', - 'message': 'Fair use please.' - } - ] - } - content = json.dumps(payload).encode('utf-8') - resp = mock_response(content) - with patch('requests.request') as mock_method: - mock_method.return_value = resp - with assert_raises(intercom.RateLimitExceeded): - Intercom.get('/users') - - @istest - def it_raises_a_service_unavailable_error(self): - payload = { - 'type': 'error.list', - 'errors': [ - { - 'type': 'service_unavailable', - 'message': 'Zzzzz.' - } - ] - } - content = json.dumps(payload).encode('utf-8') - resp = mock_response(content) - with patch('requests.request') as mock_method: - mock_method.return_value = resp - with assert_raises(intercom.ServiceUnavailableError): - Intercom.get('/users') - - @istest - def it_raises_a_multiple_matching_users_error(self): - payload = { - 'type': 'error.list', - 'errors': [ - { - 'type': 'conflict', - 'message': 'Two many cooks.' - } - ] - } - content = json.dumps(payload).encode('utf-8') - resp = mock_response(content) - with patch('requests.request') as mock_method: - mock_method.return_value = resp - with assert_raises(intercom.MultipleMatchingUsersError): - Intercom.get('/users') - - @istest - def it_handles_no_error_type(self): - payload = { - 'errors': [ - { - 'code': 'unique_user_constraint', - 'message': 'User already exists.' - } - ], - 'request_id': '00000000-0000-0000-0000-000000000000', - 'type': 'error.list' - } - content = json.dumps(payload).encode('utf-8') - resp = mock_response(content) - with patch('requests.request') as mock_method: - mock_method.return_value = resp - with assert_raises(intercom.MultipleMatchingUsersError): - Intercom.get('/users') - - payload = { - 'errors': [ - { - 'code': 'parameter_not_found', - 'message': 'missing data parameter' - } - ], - 'request_id': None, - 'type': 'error.list' - } - content = json.dumps(payload).encode('utf-8') - resp = mock_response(content) - with patch('requests.request') as mock_method: - mock_method.return_value = resp - with assert_raises(intercom.BadRequestError): - Intercom.get('/users') - - @istest - def it_handles_empty_responses(self): - resp = mock_response('', status_code=202) - with patch('requests.request') as mock_method: - mock_method.return_value = resp - Request.send_request_to_path('GET', 'events', ('x', 'y'), resp) - - resp = mock_response(' ', status_code=202) - with patch('requests.request') as mock_method: - mock_method.return_value = resp - Request.send_request_to_path('GET', 'events', ('x', 'y'), resp) - - @istest - def it_handles_no_encoding(self): - resp = mock_response( - ' ', status_code=200, encoding=None, headers=None) - resp.apparent_encoding = 'utf-8' - - with patch('requests.request') as mock_method: - mock_method.return_value = resp - Request.send_request_to_path('GET', 'events', ('x', 'y'), resp) - - @istest - def it_needs_encoding_or_apparent_encoding(self): - payload = '{}' - - if not hasattr(payload, 'decode'): - # python 3 - payload = payload.encode('utf-8') - - resp = mock_response( - payload, status_code=200, encoding=None, headers=None) - - with patch('requests.request') as mock_method: - mock_method.return_value = resp - with assert_raises(TypeError): - Request.send_request_to_path('GET', 'events', ('x', 'y'), resp) - - @istest - def it_allows_the_timeout_to_be_changed(self): - eq_(10, intercom.Request.timeout) - intercom.Request.timeout = 3 - eq_(3, intercom.Request.timeout) diff --git a/tests/unit/test_subscription.py b/tests/unit/test_subscription.py deleted file mode 100644 index b7a01ea9..00000000 --- a/tests/unit/test_subscription.py +++ /dev/null @@ -1,35 +0,0 @@ -# -*- coding: utf-8 -*- - -import unittest - -from intercom import Intercom -from intercom import Subscription -from mock import patch -from nose.tools import eq_ -from nose.tools import istest -from tests.unit import test_subscription - - -class SubscriptionTest(unittest.TestCase): - - @istest - def it_gets_a_subscription(self): - with patch.object(Intercom, 'get', return_value=test_subscription) as mock_method: # noqa - subscription = Subscription.find(id="nsub_123456789") - eq_(subscription.topics[0], "user.created") - eq_(subscription.topics[1], "conversation.user.replied") - eq_(subscription.self, - "https://api.intercom.io/subscriptions/nsub_123456789") - mock_method.assert_called_once_with('/subscriptions/nsub_123456789') # noqa - - @istest - def it_creates_a_subscription(self): - with patch.object(Intercom, 'post', return_value=test_subscription) as mock_method: # noqa - subscription = Subscription.create( - url="http://example.com", - topics=["user.created"] - ) - eq_(subscription.topics[0], "user.created") - eq_(subscription.url, "http://example.com") - mock_method.assert_called_once_with( - '/subscriptions/', url="http://example.com", topics=["user.created"]) # noqa diff --git a/tests/unit/test_tag.py b/tests/unit/test_tag.py deleted file mode 100644 index a9d36dc9..00000000 --- a/tests/unit/test_tag.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- - -import unittest - -from intercom import Intercom -from intercom import Tag -from mock import patch -from nose.tools import eq_ -from nose.tools import istest -from tests.unit import test_tag - - -class TagTest(unittest.TestCase): - - @istest - def it_gets_a_tag(self): - with patch.object(Intercom, 'get', return_value=test_tag) as mock_method: # noqa - tag = Tag.find(name="Test Tag") - eq_(tag.name, "Test Tag") - mock_method.assert_called_once_with('/tags', name="Test Tag") - - @istest - def it_creates_a_tag(self): - with patch.object(Intercom, 'post', return_value=test_tag) as mock_method: # noqa - tag = Tag.create(name="Test Tag") - eq_(tag.name, "Test Tag") - mock_method.assert_called_once_with('/tags/', name="Test Tag") - - @istest - def it_tags_users(self): - params = { - 'name': 'Test Tag', - 'user_ids': ['abc123', 'def456'], - 'tag_or_untag': 'tag' - } - with patch.object(Intercom, 'post', return_value=test_tag) as mock_method: # noqa - tag = Tag.create(**params) - eq_(tag.name, "Test Tag") - eq_(tag.tagged_user_count, 2) - mock_method.assert_called_once_with('/tags/', **params) diff --git a/tests/unit/test_user.py b/tests/unit/test_user.py deleted file mode 100644 index 59add0cf..00000000 --- a/tests/unit/test_user.py +++ /dev/null @@ -1,446 +0,0 @@ -# -*- coding: utf-8 -*- - -import json -import mock -import time -import unittest - -from datetime import datetime -from intercom.collection_proxy import CollectionProxy -from intercom.lib.flat_store import FlatStore -from intercom import Intercom -from intercom import User -from intercom import MultipleMatchingUsersError -from intercom.utils import create_class_instance -from mock import patch -from nose.tools import assert_raises -from nose.tools import eq_ -from nose.tools import ok_ -from nose.tools import istest -from tests.unit import get_user -from tests.unit import mock_response - - -class UserTest(unittest.TestCase): - - @istest - def it_to_dict_itself(self): - created_at = datetime.utcnow() - user = User( - email="jim@example.com", user_id="12345", - created_at=created_at, name="Jim Bob") - as_dict = user.to_dict - eq_(as_dict["email"], "jim@example.com") - eq_(as_dict["user_id"], "12345") - eq_(as_dict["created_at"], time.mktime(created_at.timetuple())) - eq_(as_dict["name"], "Jim Bob") - - @istest - def it_presents_created_at_and_last_impression_at_as_datetime(self): - now = datetime.utcnow() - now_ts = time.mktime(now.timetuple()) - user = User.from_api( - {'created_at': now_ts, 'last_impression_at': now_ts}) - self.assertIsInstance(user.created_at, datetime) - eq_(now.strftime('%c'), user.created_at.strftime('%c')) - self.assertIsInstance(user.last_impression_at, datetime) - eq_(now.strftime('%c'), user.last_impression_at.strftime('%c')) - - @istest - def it_throws_an_attribute_error_on_trying_to_access_an_attribute_that_has_not_been_set(self): # noqa - with assert_raises(AttributeError): - user = User() - user.foo_property - - @istest - def it_presents_a_complete_user_record_correctly(self): - user = User.from_api(get_user()) - eq_('id-from-customers-app', user.user_id) - eq_('bob@example.com', user.email) - eq_('Joe Schmoe', user.name) - eq_('the-app-id', user.app_id) - eq_(123, user.session_count) - eq_(1401970114, time.mktime(user.created_at.timetuple())) - eq_(1393613864, time.mktime(user.remote_created_at.timetuple())) - eq_(1401970114, time.mktime(user.updated_at.timetuple())) - - Avatar = create_class_instance('Avatar') # noqa - Company = create_class_instance('Company') # noqa - SocialProfile = create_class_instance('SocialProfile') # noqa - LocationData = create_class_instance('LocationData') # noqa - self.assertIsInstance(user.avatar, Avatar.__class__) - img_url = 'https://graph.facebook.com/1/picture?width=24&height=24' - eq_(img_url, user.avatar.image_url) - - self.assertIsInstance(user.companies, list) - eq_(1, len(user.companies)) - self.assertIsInstance(user.companies[0], Company.__class__) - eq_('123', user.companies[0].company_id) - eq_('bbbbbbbbbbbbbbbbbbbbbbbb', user.companies[0].id) - eq_('the-app-id', user.companies[0].app_id) - eq_('Company 1', user.companies[0].name) - eq_(1390936440, time.mktime( - user.companies[0].remote_created_at.timetuple())) - eq_(1401970114, time.mktime( - user.companies[0].created_at.timetuple())) - eq_(1401970114, time.mktime( - user.companies[0].updated_at.timetuple())) - eq_(1401970113, time.mktime( - user.companies[0].last_request_at.timetuple())) - eq_(0, user.companies[0].monthly_spend) - eq_(0, user.companies[0].session_count) - eq_(1, user.companies[0].user_count) - eq_([], user.companies[0].tag_ids) - - self.assertIsInstance(user.custom_attributes, FlatStore) - eq_('b', user.custom_attributes["a"]) - eq_(2, user.custom_attributes["b"]) - - eq_(4, len(user.social_profiles)) - twitter_account = user.social_profiles[0] - self.assertIsInstance(twitter_account, SocialProfile.__class__) - eq_('twitter', twitter_account.name) - eq_('abc', twitter_account.username) - eq_('http://twitter.com/abc', twitter_account.url) - - self.assertIsInstance(user.location_data, LocationData.__class__) - eq_('Dublin', user.location_data.city_name) - eq_('EU', user.location_data.continent_code) - eq_('Ireland', user.location_data.country_name) - eq_('90', user.location_data.latitude) - eq_('10', user.location_data.longitude) - eq_('IRL', user.location_data.country_code) - - ok_(user.unsubscribed_from_emails) - eq_("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11", user.user_agent_data) # noqa - - @istest - def it_allows_update_last_request_at(self): - payload = { - 'user_id': '1224242', - 'update_last_request_at': True, - 'custom_attributes': {} - } - with patch.object(Intercom, 'post', return_value=payload) as mock_method: - User.create(user_id='1224242', update_last_request_at=True) - mock_method.assert_called_once_with( - '/users/', update_last_request_at=True, user_id='1224242') - - @istest - def it_allows_easy_setting_of_custom_data(self): - now = datetime.utcnow() - now_ts = time.mktime(now.timetuple()) - - user = User() - user.custom_attributes["mad"] = 123 - user.custom_attributes["other"] = now_ts - user.custom_attributes["thing"] = "yay" - attrs = {"mad": 123, "other": now_ts, "thing": "yay"} - eq_(user.to_dict["custom_attributes"], attrs) - - @istest - def it_allows_easy_setting_of_multiple_companies(self): - user = User() - companies = [ - {"name": "Intercom", "company_id": "6"}, - {"name": "Test", "company_id": "9"}, - ] - user.companies = companies - eq_(user.to_dict["companies"], companies) - - @istest - def it_rejects_nested_data_structures_in_custom_attributes(self): - user = User() - with assert_raises(ValueError): - user.custom_attributes["thing"] = [1] - - with assert_raises(ValueError): - user.custom_attributes["thing"] = {1: 2} - - with assert_raises(ValueError): - user.custom_attributes = {1: {2: 3}} - - user = User.from_api(get_user()) - with assert_raises(ValueError): - user.custom_attributes["thing"] = [1] - - @istest - def it_fetches_a_user(self): - with patch.object(Intercom, 'get', return_value=get_user()) as mock_method: # noqa - user = User.find(email='somebody@example.com') - eq_(user.email, 'bob@example.com') - eq_(user.name, 'Joe Schmoe') - mock_method.assert_called_once_with('/users', email='somebody@example.com') # noqa - - @istest - # @httpretty.activate - def it_saves_a_user_always_sends_custom_attributes(self): - user = User(email="jo@example.com", user_id="i-1224242") - - body = { - 'email': 'jo@example.com', - 'user_id': 'i-1224242', - 'custom_attributes': {} - } - - with patch.object(Intercom, 'post', return_value=body) as mock_method: - user.save() - eq_(user.email, 'jo@example.com') - eq_(user.custom_attributes, {}) - mock_method.assert_called_once_with( - '/users', - email="jo@example.com", user_id="i-1224242", - custom_attributes={}) - - @istest - def it_saves_a_user_with_a_company(self): - user = User( - email="jo@example.com", user_id="i-1224242", - company={'company_id': 6, 'name': 'Intercom'}) - - body = { - 'email': 'jo@example.com', - 'user_id': 'i-1224242', - 'companies': [{ - 'company_id': 6, - 'name': 'Intercom' - }] - } - with patch.object(Intercom, 'post', return_value=body) as mock_method: - user.save() - eq_(user.email, 'jo@example.com') - eq_(len(user.companies), 1) - mock_method.assert_called_once_with( - '/users', - email="jo@example.com", user_id="i-1224242", - company={'company_id': 6, 'name': 'Intercom'}, - custom_attributes={}) - - @istest - def it_saves_a_user_with_companies(self): - user = User( - email="jo@example.com", user_id="i-1224242", - companies=[{'company_id': 6, 'name': 'Intercom'}]) - body = { - 'email': 'jo@example.com', - 'user_id': 'i-1224242', - 'companies': [{ - 'company_id': 6, - 'name': 'Intercom' - }] - } - with patch.object(Intercom, 'post', return_value=body) as mock_method: - user.save() - eq_(user.email, 'jo@example.com') - eq_(len(user.companies), 1) - mock_method.assert_called_once_with( - '/users', - email="jo@example.com", user_id="i-1224242", - companies=[{'company_id': 6, 'name': 'Intercom'}], - custom_attributes={}) - - @istest - def it_can_save_a_user_with_a_none_email(self): - user = User( - email=None, user_id="i-1224242", - companies=[{'company_id': 6, 'name': 'Intercom'}]) - body = { - 'custom_attributes': {}, - 'email': None, - 'user_id': 'i-1224242', - 'companies': [{ - 'company_id': 6, - 'name': 'Intercom' - }] - } - with patch.object(Intercom, 'post', return_value=body) as mock_method: - user.save() - ok_(user.email is None) - eq_(user.user_id, 'i-1224242') - mock_method.assert_called_once_with( - '/users', - email=None, user_id="i-1224242", - companies=[{'company_id': 6, 'name': 'Intercom'}], - custom_attributes={}) - - @istest - def it_deletes_a_user(self): - user = User(id="1") - with patch.object(Intercom, 'delete', return_value={}) as mock_method: - user = user.delete() - eq_(user.id, "1") - mock_method.assert_called_once_with('/users/1/') - - @istest - def it_can_use_user_create_for_convenience(self): - payload = { - 'email': 'jo@example.com', - 'user_id': 'i-1224242', - 'custom_attributes': {} - } - with patch.object(Intercom, 'post', return_value=payload) as mock_method: # noqa - user = User.create(email="jo@example.com", user_id="i-1224242") - eq_(payload, user.to_dict) - mock_method.assert_called_once_with('/users/', email="jo@example.com", user_id="i-1224242") # noqa - - @istest - def it_updates_the_user_with_attributes_set_by_the_server(self): - payload = { - 'email': 'jo@example.com', - 'user_id': 'i-1224242', - 'custom_attributes': {}, - 'session_count': 4 - } - with patch.object(Intercom, 'post', return_value=payload) as mock_method: - user = User.create(email="jo@example.com", user_id="i-1224242") - eq_(payload, user.to_dict) - mock_method.assert_called_once_with('/users/', email="jo@example.com", user_id="i-1224242") # noqa - - @istest - def it_allows_setting_dates_to_none_without_converting_them_to_0(self): - payload = { - 'email': 'jo@example.com', - 'custom_attributes': {}, - 'remote_created_at': None - } - with patch.object(Intercom, 'post', return_value=payload) as mock_method: - user = User.create(email="jo@example.com", remote_created_at=None) - ok_(user.remote_created_at is None) - mock_method.assert_called_once_with('/users/', email="jo@example.com", remote_created_at=None) # noqa - - @istest - def it_gets_sets_rw_keys(self): - created_at = datetime.utcnow() - payload = { - 'email': 'me@example.com', - 'user_id': 'abc123', - 'name': 'Bob Smith', - 'last_seen_ip': '1.2.3.4', - 'last_seen_user_agent': 'ie6', - 'created_at': time.mktime(created_at.timetuple()) - } - user = User(**payload) - expected_keys = ['custom_attributes'] - expected_keys.extend(list(payload.keys())) - eq_(sorted(expected_keys), sorted(user.to_dict.keys())) - for key in list(payload.keys()): - eq_(payload[key], user.to_dict[key]) - - @istest - def it_will_allow_extra_attributes_in_response_from_api(self): - user = User.from_api({'new_param': 'some value'}) - eq_('some value', user.new_param) - - @istest - def it_returns_a_collectionproxy_for_all_without_making_any_requests(self): - with mock.patch('intercom.Request.send_request_to_path', new_callable=mock.NonCallableMock): # noqa - res = User.all() - self.assertIsInstance(res, CollectionProxy) - - @istest - def it_returns_the_total_number_of_users(self): - with mock.patch.object(User, 'count') as mock_count: - mock_count.return_value = 100 - eq_(100, User.count()) - - @istest - def it_raises_a_multiple_matching_users_error_when_receiving_a_conflict(self): # noqa - payload = { - 'type': 'error.list', - 'errors': [ - { - 'code': 'conflict', - 'message': 'Multiple existing users match this email address - must be more specific using user_id' # noqa - } - ] - } - # create bytes content - content = json.dumps(payload).encode('utf-8') - # create mock response - resp = mock_response(content) - with patch('requests.request') as mock_method: - mock_method.return_value = resp - with assert_raises(MultipleMatchingUsersError): - Intercom.get('/users') - - @istest - def it_handles_accented_characters(self): - # create a user dict with a name that contains accented characters - payload = get_user(name='Jóe Schmö') - # create bytes content - content = json.dumps(payload).encode('utf-8') - # create mock response - resp = mock_response(content) - with patch('requests.request') as mock_method: - mock_method.return_value = resp - user = User.find(email='bob@example.com') - try: - # Python 2 - eq_(unicode('Jóe Schmö', 'utf-8'), user.name) - except NameError: - # Python 3 - eq_('Jóe Schmö', user.name) - - -class DescribeIncrementingCustomAttributeFields(unittest.TestCase): - - def setUp(self): # noqa - created_at = datetime.utcnow() - params = { - 'email': 'jo@example.com', - 'user_id': 'i-1224242', - 'custom_attributes': { - 'mad': 123, - 'another': 432, - 'other': time.mktime(created_at.timetuple()), - 'thing': 'yay' - } - } - self.user = User(**params) - - @istest - def it_increments_up_by_1_with_no_args(self): - self.user.increment('mad') - eq_(self.user.to_dict['custom_attributes']['mad'], 124) - - @istest - def it_increments_up_by_given_value(self): - self.user.increment('mad', 4) - eq_(self.user.to_dict['custom_attributes']['mad'], 127) - - @istest - def it_increments_down_by_given_value(self): - self.user.increment('mad', -1) - eq_(self.user.to_dict['custom_attributes']['mad'], 122) - - @istest - def it_can_increment_new_custom_data_fields(self): - self.user.increment('new_field', 3) - eq_(self.user.to_dict['custom_attributes']['new_field'], 3) - - @istest - def it_can_call_increment_on_the_same_key_twice_and_increment_by_2(self): # noqa - self.user.increment('mad') - self.user.increment('mad') - eq_(self.user.to_dict['custom_attributes']['mad'], 125) - - @istest - def it_can_save_after_increment(self): # noqa - user = User( - email=None, user_id="i-1224242", - companies=[{'company_id': 6, 'name': 'Intercom'}]) - body = { - 'custom_attributes': {}, - 'email': "", - 'user_id': 'i-1224242', - 'companies': [{ - 'company_id': 6, - 'name': 'Intercom' - }] - } - with patch.object(Intercom, 'post', return_value=body) as mock_method: # noqa - user.increment('mad') - eq_(user.to_dict['custom_attributes']['mad'], 1) - user.save() - ok_('email' not in user.identity_hash) - ok_('user_id' in user.identity_hash) diff --git a/tests/unit/traits/__init__.py b/tests/unit/traits/__init__.py deleted file mode 100644 index 40a96afc..00000000 --- a/tests/unit/traits/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/tests/unit/traits/test_api_resource.py b/tests/unit/traits/test_api_resource.py deleted file mode 100644 index 92a73ee5..00000000 --- a/tests/unit/traits/test_api_resource.py +++ /dev/null @@ -1,86 +0,0 @@ -# -*- coding: utf-8 -*- - -import unittest - -from datetime import datetime -from intercom.traits.api_resource import Resource -from nose.tools import assert_raises -from nose.tools import eq_ -from nose.tools import ok_ -from nose.tools import istest - - -class IntercomTraitsApiResource(unittest.TestCase): - - def setUp(self): # noqa - self.object_json = { - "type": "company", - "id": "aaaaaaaaaaaaaaaaaaaaaaaa", - "app_id": "some-app-id", - "name": "SuperSuite", - "plan_id": 1, - "remote_company_id": "8", - "remote_created_at": 103201, - "created_at": 1374056196, - "user_count": 1, - "custom_attributes": {} - } - self.api_resource = Resource().from_response(self.object_json) - self.api_resource_obj = super(Resource, self.api_resource) - - @istest - def it_does_not_set_type_on_parsing_json(self): - ok_(not hasattr(self.api_resource, 'type')) - - @istest - def it_coerces_time_on_parsing_json(self): - eq_(datetime.fromtimestamp(1374056196), self.api_resource.created_at) - - @istest - def it_dynamically_defines_accessors_for_non_existent_properties(self): - ok_(not hasattr(self.api_resource, 'spiders')) - self.api_resource.spiders = 4 - ok_(hasattr(self.api_resource, 'spiders')) - - @istest - def it_calls_dynamically_defined_getter_when_asked(self): - self.api_resource.foo = 4 - eq_(4, self.api_resource.foo) - - @istest - def it_accepts_unix_timestamps_into_dynamically_defined_date_setters(self): - self.api_resource.foo_at = 1401200468 - eq_(1401200468, self.api_resource_obj.__getattribute__('foo_at')) - - @istest - def it_exposes_dates_correctly_for_dynamically_defined_getters(self): - self.api_resource.foo_at = 1401200468 - eq_(datetime.fromtimestamp(1401200468), self.api_resource.foo_at) - - # @istest - # def it_throws_regular_error_when_non_existant_getter_is_called_that_is_backed_by_an_instance_variable(self): # noqa - # super(Resource, self.api_resource).__setattr__('bar', 'you cant see me') # noqa - # print (self.api_resource.bar) - - @istest - def it_throws_attribute_error_when_non_existent_attribute_is_called(self): - with assert_raises(AttributeError): - self.api_resource.flubber - - @istest - def it_throws_attribute_error_when_non_existent_method_is_called(self): - with assert_raises(AttributeError): - self.api_resource.flubber() - - @istest - def it_throws_attribute_error_when_non_existent_setter_is_called(self): - with assert_raises(AttributeError): - self.api_resource.flubber('a', 'b') - - @istest - def it_create_an_initialized_resource_equal_to_a_from_response_resource(self): # noqa - initialized_api_resource = Resource(**self.object_json) - for key in list(self.object_json.keys()): - if key == "type": - continue - eq_(getattr(initialized_api_resource, key), getattr(self.api_resource, key)) # noqa diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py new file mode 100644 index 00000000..f3ea2659 --- /dev/null +++ b/tests/utils/__init__.py @@ -0,0 +1,2 @@ +# This file was auto-generated by Fern from our API Definition. + diff --git a/tests/utils/assets/models/__init__.py b/tests/utils/assets/models/__init__.py new file mode 100644 index 00000000..2cf01263 --- /dev/null +++ b/tests/utils/assets/models/__init__.py @@ -0,0 +1,21 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +from .circle import CircleParams +from .object_with_defaults import ObjectWithDefaultsParams +from .object_with_optional_field import ObjectWithOptionalFieldParams +from .shape import Shape_CircleParams, Shape_SquareParams, ShapeParams +from .square import SquareParams +from .undiscriminated_shape import UndiscriminatedShapeParams + +__all__ = [ + "CircleParams", + "ObjectWithDefaultsParams", + "ObjectWithOptionalFieldParams", + "ShapeParams", + "Shape_CircleParams", + "Shape_SquareParams", + "SquareParams", + "UndiscriminatedShapeParams", +] diff --git a/tests/utils/assets/models/circle.py b/tests/utils/assets/models/circle.py new file mode 100644 index 00000000..36a0fd74 --- /dev/null +++ b/tests/utils/assets/models/circle.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +import typing_extensions + +from intercom.core.serialization import FieldMetadata + + +class CircleParams(typing_extensions.TypedDict): + radius_measurement: typing_extensions.Annotated[float, FieldMetadata(alias="radiusMeasurement")] diff --git a/tests/utils/assets/models/color.py b/tests/utils/assets/models/color.py new file mode 100644 index 00000000..2aa2c4c5 --- /dev/null +++ b/tests/utils/assets/models/color.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +import typing + +Color = typing.Union[typing.Literal["red", "blue"], typing.Any] diff --git a/tests/utils/assets/models/object_with_defaults.py b/tests/utils/assets/models/object_with_defaults.py new file mode 100644 index 00000000..a977b1d2 --- /dev/null +++ b/tests/utils/assets/models/object_with_defaults.py @@ -0,0 +1,15 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +import typing_extensions + + +class ObjectWithDefaultsParams(typing_extensions.TypedDict): + """ + Defines properties with default values and validation rules. + """ + + decimal: typing_extensions.NotRequired[float] + string: typing_extensions.NotRequired[str] + required_string: str diff --git a/tests/utils/assets/models/object_with_optional_field.py b/tests/utils/assets/models/object_with_optional_field.py new file mode 100644 index 00000000..89983fe2 --- /dev/null +++ b/tests/utils/assets/models/object_with_optional_field.py @@ -0,0 +1,35 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing +import uuid + +import typing_extensions +from .color import Color +from .shape import ShapeParams +from .undiscriminated_shape import UndiscriminatedShapeParams + +from intercom.core.serialization import FieldMetadata + + +class ObjectWithOptionalFieldParams(typing_extensions.TypedDict): + literal: typing.Literal["lit_one"] + string: typing_extensions.NotRequired[str] + integer: typing_extensions.NotRequired[int] + long_: typing_extensions.NotRequired[typing_extensions.Annotated[int, FieldMetadata(alias="long")]] + double: typing_extensions.NotRequired[float] + bool_: typing_extensions.NotRequired[typing_extensions.Annotated[bool, FieldMetadata(alias="bool")]] + datetime: typing_extensions.NotRequired[dt.datetime] + date: typing_extensions.NotRequired[dt.date] + uuid_: typing_extensions.NotRequired[typing_extensions.Annotated[uuid.UUID, FieldMetadata(alias="uuid")]] + base_64: typing_extensions.NotRequired[typing_extensions.Annotated[str, FieldMetadata(alias="base64")]] + list_: typing_extensions.NotRequired[typing_extensions.Annotated[typing.Sequence[str], FieldMetadata(alias="list")]] + set_: typing_extensions.NotRequired[typing_extensions.Annotated[typing.Set[str], FieldMetadata(alias="set")]] + map_: typing_extensions.NotRequired[typing_extensions.Annotated[typing.Dict[int, str], FieldMetadata(alias="map")]] + enum: typing_extensions.NotRequired[Color] + union: typing_extensions.NotRequired[ShapeParams] + second_union: typing_extensions.NotRequired[ShapeParams] + undiscriminated_union: typing_extensions.NotRequired[UndiscriminatedShapeParams] + any: typing.Optional[typing.Any] diff --git a/tests/utils/assets/models/shape.py b/tests/utils/assets/models/shape.py new file mode 100644 index 00000000..4e484bd2 --- /dev/null +++ b/tests/utils/assets/models/shape.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +import typing_extensions + +from intercom.core.serialization import FieldMetadata + + +class Base(typing_extensions.TypedDict): + id: str + + +class Shape_CircleParams(Base): + shape_type: typing_extensions.Annotated[typing.Literal["circle"], FieldMetadata(alias="shapeType")] + radius_measurement: typing_extensions.Annotated[float, FieldMetadata(alias="radiusMeasurement")] + + +class Shape_SquareParams(Base): + shape_type: typing_extensions.Annotated[typing.Literal["square"], FieldMetadata(alias="shapeType")] + length_measurement: typing_extensions.Annotated[float, FieldMetadata(alias="lengthMeasurement")] + + +ShapeParams = typing.Union[Shape_CircleParams, Shape_SquareParams] diff --git a/tests/utils/assets/models/square.py b/tests/utils/assets/models/square.py new file mode 100644 index 00000000..f1afcc93 --- /dev/null +++ b/tests/utils/assets/models/square.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +import typing_extensions + +from intercom.core.serialization import FieldMetadata + + +class SquareParams(typing_extensions.TypedDict): + length_measurement: typing_extensions.Annotated[float, FieldMetadata(alias="lengthMeasurement")] diff --git a/tests/utils/assets/models/undiscriminated_shape.py b/tests/utils/assets/models/undiscriminated_shape.py new file mode 100644 index 00000000..99f12b30 --- /dev/null +++ b/tests/utils/assets/models/undiscriminated_shape.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .circle import CircleParams +from .square import SquareParams + +UndiscriminatedShapeParams = typing.Union[CircleParams, SquareParams] diff --git a/tests/utils/test_http_client.py b/tests/utils/test_http_client.py new file mode 100644 index 00000000..e997a4d1 --- /dev/null +++ b/tests/utils/test_http_client.py @@ -0,0 +1,109 @@ +# This file was auto-generated by Fern from our API Definition. + +from intercom.core.http_client import get_request_body, remove_none_from_dict +from intercom.core.request_options import RequestOptions + + +def get_request_options() -> RequestOptions: + return {"additional_body_parameters": {"see you": "later"}} + + +def get_request_options_with_none() -> RequestOptions: + return {"additional_body_parameters": {"see you": "later", "optional": None}} + + +def test_get_json_request_body() -> None: + json_body, data_body = get_request_body(json={"hello": "world"}, data=None, request_options=None, omit=None) + assert json_body == {"hello": "world"} + assert data_body is None + + json_body_extras, data_body_extras = get_request_body( + json={"goodbye": "world"}, data=None, request_options=get_request_options(), omit=None + ) + + assert json_body_extras == {"goodbye": "world", "see you": "later"} + assert data_body_extras is None + + +def test_get_files_request_body() -> None: + json_body, data_body = get_request_body(json=None, data={"hello": "world"}, request_options=None, omit=None) + assert data_body == {"hello": "world"} + assert json_body is None + + json_body_extras, data_body_extras = get_request_body( + json=None, data={"goodbye": "world"}, request_options=get_request_options(), omit=None + ) + + assert data_body_extras == {"goodbye": "world", "see you": "later"} + assert json_body_extras is None + + +def test_get_none_request_body() -> None: + json_body, data_body = get_request_body(json=None, data=None, request_options=None, omit=None) + assert data_body is None + assert json_body is None + + json_body_extras, data_body_extras = get_request_body( + json=None, data=None, request_options=get_request_options(), omit=None + ) + + assert json_body_extras == {"see you": "later"} + assert data_body_extras is None + + +def test_get_empty_json_request_body() -> None: + unrelated_request_options: RequestOptions = {"max_retries": 3} + json_body, data_body = get_request_body(json=None, data=None, request_options=unrelated_request_options, omit=None) + assert json_body is None + assert data_body is None + + json_body_extras, data_body_extras = get_request_body( + json={}, data=None, request_options=unrelated_request_options, omit=None + ) + + assert json_body_extras is None + assert data_body_extras is None + + +def test_json_body_preserves_none_values() -> None: + """Test that JSON bodies preserve None values (they become JSON null).""" + json_body, data_body = get_request_body( + json={"hello": "world", "optional": None}, data=None, request_options=None, omit=None + ) + # JSON bodies should preserve None values + assert json_body == {"hello": "world", "optional": None} + assert data_body is None + + +def test_data_body_preserves_none_values_without_multipart() -> None: + """Test that data bodies preserve None values when not using multipart. + + The filtering of None values happens in HttpClient.request/stream methods, + not in get_request_body. This test verifies get_request_body doesn't filter None. + """ + json_body, data_body = get_request_body( + json=None, data={"hello": "world", "optional": None}, request_options=None, omit=None + ) + # get_request_body should preserve None values in data body + # The filtering happens later in HttpClient.request when multipart is detected + assert data_body == {"hello": "world", "optional": None} + assert json_body is None + + +def test_remove_none_from_dict_filters_none_values() -> None: + """Test that remove_none_from_dict correctly filters out None values.""" + original = {"hello": "world", "optional": None, "another": "value", "also_none": None} + filtered = remove_none_from_dict(original) + assert filtered == {"hello": "world", "another": "value"} + # Original should not be modified + assert original == {"hello": "world", "optional": None, "another": "value", "also_none": None} + + +def test_remove_none_from_dict_empty_dict() -> None: + """Test that remove_none_from_dict handles empty dict.""" + assert remove_none_from_dict({}) == {} + + +def test_remove_none_from_dict_all_none() -> None: + """Test that remove_none_from_dict handles dict with all None values.""" + assert remove_none_from_dict({"a": None, "b": None}) == {} diff --git a/tests/utils/test_query_encoding.py b/tests/utils/test_query_encoding.py new file mode 100644 index 00000000..751c3894 --- /dev/null +++ b/tests/utils/test_query_encoding.py @@ -0,0 +1,36 @@ +# This file was auto-generated by Fern from our API Definition. + +from intercom.core.query_encoder import encode_query + + +def test_query_encoding_deep_objects() -> None: + assert encode_query({"hello world": "hello world"}) == [("hello world", "hello world")] + assert encode_query({"hello_world": {"hello": "world"}}) == [("hello_world[hello]", "world")] + assert encode_query({"hello_world": {"hello": {"world": "today"}, "test": "this"}, "hi": "there"}) == [ + ("hello_world[hello][world]", "today"), + ("hello_world[test]", "this"), + ("hi", "there"), + ] + + +def test_query_encoding_deep_object_arrays() -> None: + assert encode_query({"objects": [{"key": "hello", "value": "world"}, {"key": "foo", "value": "bar"}]}) == [ + ("objects[key]", "hello"), + ("objects[value]", "world"), + ("objects[key]", "foo"), + ("objects[value]", "bar"), + ] + assert encode_query( + {"users": [{"name": "string", "tags": ["string"]}, {"name": "string2", "tags": ["string2", "string3"]}]} + ) == [ + ("users[name]", "string"), + ("users[tags]", "string"), + ("users[name]", "string2"), + ("users[tags]", "string2"), + ("users[tags]", "string3"), + ] + + +def test_encode_query_with_none() -> None: + encoded = encode_query(None) + assert encoded is None diff --git a/tests/utils/test_serialization.py b/tests/utils/test_serialization.py new file mode 100644 index 00000000..e5069317 --- /dev/null +++ b/tests/utils/test_serialization.py @@ -0,0 +1,72 @@ +# This file was auto-generated by Fern from our API Definition. + +from typing import Any, List + +from .assets.models import ObjectWithOptionalFieldParams, ShapeParams + +from intercom.core.serialization import convert_and_respect_annotation_metadata + +UNION_TEST: ShapeParams = {"radius_measurement": 1.0, "shape_type": "circle", "id": "1"} +UNION_TEST_CONVERTED = {"shapeType": "circle", "radiusMeasurement": 1.0, "id": "1"} + + +def test_convert_and_respect_annotation_metadata() -> None: + data: ObjectWithOptionalFieldParams = { + "string": "string", + "long_": 12345, + "bool_": True, + "literal": "lit_one", + "any": "any", + } + converted = convert_and_respect_annotation_metadata( + object_=data, annotation=ObjectWithOptionalFieldParams, direction="write" + ) + assert converted == {"string": "string", "long": 12345, "bool": True, "literal": "lit_one", "any": "any"} + + +def test_convert_and_respect_annotation_metadata_in_list() -> None: + data: List[ObjectWithOptionalFieldParams] = [ + {"string": "string", "long_": 12345, "bool_": True, "literal": "lit_one", "any": "any"}, + {"string": "another string", "long_": 67890, "list_": [], "literal": "lit_one", "any": "any"}, + ] + converted = convert_and_respect_annotation_metadata( + object_=data, annotation=List[ObjectWithOptionalFieldParams], direction="write" + ) + + assert converted == [ + {"string": "string", "long": 12345, "bool": True, "literal": "lit_one", "any": "any"}, + {"string": "another string", "long": 67890, "list": [], "literal": "lit_one", "any": "any"}, + ] + + +def test_convert_and_respect_annotation_metadata_in_nested_object() -> None: + data: ObjectWithOptionalFieldParams = { + "string": "string", + "long_": 12345, + "union": UNION_TEST, + "literal": "lit_one", + "any": "any", + } + converted = convert_and_respect_annotation_metadata( + object_=data, annotation=ObjectWithOptionalFieldParams, direction="write" + ) + + assert converted == { + "string": "string", + "long": 12345, + "union": UNION_TEST_CONVERTED, + "literal": "lit_one", + "any": "any", + } + + +def test_convert_and_respect_annotation_metadata_in_union() -> None: + converted = convert_and_respect_annotation_metadata(object_=UNION_TEST, annotation=ShapeParams, direction="write") + + assert converted == UNION_TEST_CONVERTED + + +def test_convert_and_respect_annotation_metadata_with_empty_object() -> None: + data: Any = {} + converted = convert_and_respect_annotation_metadata(object_=data, annotation=ShapeParams, direction="write") + assert converted == data