diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..ae6ec60
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,48 @@
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+name: Release to PyPI
+
+on:
+ push:
+ tags:
+ - '*'
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Setup Python # Set Python version
+ uses: actions/setup-python@v3
+ with:
+ python-version: 3.11
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip poetry
+ poetry config virtualenvs.in-project --unset
+ make install
+
+ - name: Build
+ run: |
+ make build
+
+ - name: Format
+ run: |
+ make format
+
+ - name: Get the version
+ id: get_version
+ run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
+
+ - name: Upload artifact
+ uses: actions/upload-artifact@v3
+ with:
+ name: ${{ steps.get_version.outputs.VERSION }}
+ path: dist
+
+ - name: Release to PyPI
+ run: |
+ pip install twine
+ twine upload -u ${{ secrets.PYPI_USERNAME }} -p ${{ secrets.PYPI_PASSWORD }} --verbose dist/*
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0fc4953
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,68 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*,cover
+.hypothesis/
+venv/
+.venv/
+.python-version
+.pytest_cache
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+#Ipython Notebook
+.ipynb_checkpoints
+
+tests/
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f7ecb5c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 AfterShip
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..a867ce1
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,17 @@
+.PHONY: build
+
+install:
+ poetry install
+
+test:
+ poetry run pytest tests/ -v
+
+record:
+ poetry run pytest tests/ --vcr-record=new_episodes
+
+format:
+ poetry run ruff check --fix
+ poetry run ruff format
+
+build:
+ poetry build
diff --git a/README.md b/README.md
index 8baed55..9f07aa8 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,307 @@
-# tracking-sdk-python
\ No newline at end of file
+# AfterShip Tracking API library for Python
+
+This library allows you to quickly and easily use the AfterShip Tracking API via Python.
+
+For updates to this library, see our [GitHub release page](https://github.com/AfterShip/tracking-sdk-python/releases).
+
+If you need support using AfterShip products, please contact support@aftership.com.
+
+## Table of Contents
+
+- [AfterShip Tracking API library for Python](#aftership-tracking-api-library-for-python)
+ - [Table of Contents](#table-of-contents)
+ - [Before you begin](#before-you-begin)
+ - [Quick Start](#quick-start)
+ - [Installation](#installation)
+ - [Usage](#usage)
+ - [Constructor](#constructor)
+ - [Example](#example)
+ - [Rate Limiter](#rate-limiter)
+ - [Error Handling](#error-handling)
+ - [Error List](#error-list)
+ - [Endpoints](#endpoints)
+ - [/trackings](#trackings)
+ - [/couriers](#couriers)
+ - [/last\_checkpoint](#last_checkpoint)
+ - [/notifications](#notifications)
+ - [/estimated-delivery-date](#estimated-delivery-date)
+ - [Help](#help)
+ - [License](#license)
+
+
+## Before you begin
+
+Before you begin to integrate:
+
+- [Create an AfterShip account](https://admin.aftership.com/).
+- [Create an API key](https://organization.automizely.com/api-keys).
+- [Install Python](https://www.python.org/downloads/) version 3.8 or later.
+
+## Quick Start
+
+### Installation
+```bash
+pip install aftership-tracking-sdk
+```
+
+### Usage
+```python
+import tracking
+from tracking import exceptions
+
+try:
+ sdk = tracking.Client(
+ tracking.Configuration(
+ api_key="YOUR_API_KEY",
+ authentication_type=tracking.ApiKey,
+ )
+ )
+ result = sdk.tracking.get_tracking_by_id("qshqj7p2ugqhclxlg4ef3004")
+ print(result)
+except exceptions.InvalidOptionError:
+ pass
+except exceptions.InvalidApiKeyError:
+ pass
+except exceptions.RateLimitExceedError:
+ pass
+```
+
+
+## Constructor
+
+Create AfterShip instance with options
+
+| Name | Type | Required | Description |
+|------------|--------|----------|-----------------------------------------------------------------------------------------------------------------------------------|
+| api_key | string | ✔ | Your AfterShip API key |
+| auth_type | enum | | Default value: `AuthType.API_KEY`
AES authentication: `AuthType.AES`
RSA authentication: `AuthType.RSA` |
+| api_secret | string | | Required if the authentication type is `AuthType.AES` or `AuthType.RSA` |
+| domain | string | | AfterShip API domain. Default value: https://api.aftership.com |
+| user_agent | string | | User-defined user-agent string, please follow [RFC9110](https://www.rfc-editor.org/rfc/rfc9110#field.user-agent) format standard. |
+| proxy | string | | HTTP proxy URL to use for requests.
Default value: `null`
Example: `http://192.168.0.100:8888` |
+| max_retry | number | | Number of retries for each request. Default value: 2. Min is 0, Max is 10. |
+| timeout | number | | Timeout for each request in milliseconds. |
+
+### Example
+
+```python
+import tracking
+
+sdk = tracking.Client(
+ tracking.Configuration(
+ api_key="YOUR_API_KEY",
+ api_secret="YOUR_API_SECRET",
+ authentication_type=tracking.Aes,
+ )
+)
+```
+
+## Rate Limiter
+
+See the [Rate Limit](https://www.aftership.com/docs/aftership/quickstart/rate-limit) to understand the AfterShip rate limit policy.
+
+## Error Handling
+
+The SDK will return an error object when there is any error during the request, with the following specification:
+
+| Name | Type | Description |
+|---------------|--------|--------------------------------|
+| message | string | Detail message of the error |
+| code | enum | Error code enum for API Error. |
+| meta_code | number | API response meta code. |
+| status_code | number | HTTP status code. |
+| response_body | string | API response body. |
+
+
+### Error List
+
+| code | meta_code | status_code | message |
+|-----------------------------------|------------------|-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| INVALID_REQUEST | 400 | 400 | The request was invalid or cannot be otherwise served. |
+| INVALID_JSON | 4001 | 400 | Invalid JSON data. |
+| TRACKING_ALREADY_EXIST | 4003 | 400 | Tracking already exists. |
+| TRACKING_DOES_NOT_EXIST | 4004 | 404 | Tracking does not exist. |
+| TRACKING_NUMBER_INVALID | 4005 | 400 | The value of tracking_number is invalid. |
+| TRACKING_REQUIRED | 4006 | 400 | tracking object is required. |
+| TRACKING_NUMBER_REQUIRED | 4007 | 400 | tracking_number is required. |
+| VALUE_INVALID | 4008 | 400 | The value of [field_name] is invalid. |
+| VALUE_REQUIRED | 4009 | 400 | [field_name] is required. |
+| SLUG_INVALID | 4010 | 400 | The value of slug is invalid. |
+| MISSING_OR_INVALID_REQUIRED_FIELD | 4011 | 400 | Missing or invalid value of the required fields for this courier. Besides tracking_number, also required: [field_name] |
+| BAD_COURIER | 4012 | 400 | The error message will be one of the following:
1. Unable to import shipment as the carrier is not on your approved list for carrier auto-detection. Add the carrier here: https://admin.aftership.com/settings/couriers
2. Unable to import shipment as we don’t recognize the carrier from this tracking number.
3. Unable to import shipment as the tracking number has an invalid format.
4. Unable to import shipment as this carrier is no longer supported.
5. Unable to import shipment as the tracking number does not belong to a carrier in that group. |
+| INACTIVE_RETRACK_NOT_ALLOWED | 4013 | 400 | Retrack is not allowed. You can only retrack an inactive tracking. |
+| NOTIFICATION_REUQIRED | 4014 | 400 | notification object is required. |
+| ID_INVALID | 4015 | 400 | The value of id is invalid. |
+| RETRACK_ONCE_ALLOWED | 4016 | 400 | Retrack is not allowed. You can only retrack each shipment once. |
+| TRACKING_NUMBER_FORMAT_INVALID | 4017 | 400 | The format of tracking_number is invalid. |
+| API_KEY_INVALID | 401 | 401 | The API key is invalid. |
+| REQUEST_NOT_ALLOWED | 403 | 403 | The request is understood, but it has been refused or access is not allowed. |
+| NOT_FOUND | 404 | 404 | The URI requested is invalid or the resource requested does not exist. |
+| TOO_MANY_REQUEST | 429 | 429 | You have exceeded the API call rate limit. The default limit is 10 requests per second. |
+| INTERNAL_ERROR | 500 502 503 504 | 500 502 503 504 | Something went wrong on AfterShip's end. |
+
+## Endpoints
+
+The AfterShip instance has the following properties which are exactly the same as the API endpoints:
+
+- courier - Get a list of our supported couriers.
+- tracking - Create trackings, update trackings, and get tracking results.
+- last_checkpoint - Get tracking information of the last checkpoint of a tracking.
+- notification - Get, add or remove contacts (sms or email) to be notified when the status of a tracking has changed.
+- estimated-delivery-date - Get estimated delivery date for your order.
+
+
+### /trackings
+
+**POST** /trackings
+
+```python
+import tracking
+from tracking import exceptions
+
+try:
+ sdk = tracking.Client(
+ tracking.Configuration(
+ api_key="asak_da8a673f24ff4475a44defe7bd3d2de7",
+ api_secret="assk_33532102503846b28e879455d0e0122e",
+ authentication_type=tracking.Aes,
+ )
+ )
+ data = tracking.TrackingCreateTrackingRequest()
+ data.tracking_number = "9505513461174170617209"
+ data.slug = "usps"
+ result = sdk.tracking.create_tracking(data)
+ print(result)
+except exceptions.InvalidOptionError:
+ pass
+```
+
+**DELETE** /trackings/:id
+
+```python
+sdk.tracking.delete_tracking_by_id("pugd4lue1oxtjlxlphas600f")
+```
+
+**GET** /trackings
+
+```python
+result = sdk.tracking.get_trackings(keyword="1234")
+print(result)
+```
+
+**GET** /trackings/:id
+
+```python
+result = sdk.tracking.get_tracking_by_id("rft4xu2rs1um1lwhm8j1p02r")
+print(result)
+```
+
+**PUT** /trackings/:id
+
+```python
+data = tracking.TrackingUpdateTrackingByIdRequest()
+data.note = "test"
+result = sdk.tracking.update_tracking_by_id("hqhyzb21sm0colweuats7001", data)
+print(result)
+```
+
+**POST** /trackings/:id/retrack
+
+```python
+result = sdk.tracking.retrack_tracking_by_id("hqhyzb21sm0colweuats7001")
+print(result)
+```
+
+**POST** /trackings/:id/mark-as-completed
+
+```python
+data = tracking.MarkTrackingCompletedByIdRequest()
+data.reason = "DELIVERED"
+result = sdk.tracking.mark_tracking_completed_by_id("hqhyzb21sm0colweuats7001", data)
+print(result)
+```
+
+### /couriers
+**GET** /couriers
+
+```python
+result = sdk.courier.get_user_couriers()
+print(result)
+```
+
+**GET** /couriers/all
+
+```python
+result = sdk.courier.get_all_couriers()
+print(result)
+```
+
+**POST** /couriers/detect
+
+```python
+data = tracking.TrackingDetectCourierRequest()
+data.tracking_number = "9434609105464265845274"
+result = sdk.courier.detect_courier(data)
+print(result)
+```
+
+### /last_checkpoint
+
+**GET** /last_checkpoint/:id
+
+```python
+result = sdk.last_checkpoint.get_checkpoint_by_tracking_id("qshqj7p2ugqhclxlg4ef3004")
+print(result)
+```
+
+### /notifications
+
+**GET** /notifications/:id
+
+```python
+result = sdk.notification.get_notification_by_tracking_id("qshqj7p2ugqhclxlg4ef3004")
+print(result)
+```
+
+**POST** /notifications/:id/add
+
+```python
+data = tracking.NotificationRequestV1()
+data.emails = ["test@gmail.com"]
+result = sdk.notification.add_notification_by_tracking_id("qshqj7p2ugqhclxlg4ef3004", data)
+print(result)
+```
+
+**POST** /notifications/:id/remove
+
+```python
+data = tracking.NotificationRequestV1()
+data.emails = ["123@gmail.com"]
+result = sdk.notification.delete_notification_by_tracking_id("kponlnb1w64fmlxlakyln00l", data)
+print(result)
+```
+
+### /estimated-delivery-date
+
+**POST** /estimated-delivery-date/predict-batch
+
+```python
+req = tracking.PredictBatchRequest()
+date = tracking.EstimatedDeliveryDateRequest()
+date.slug = 'usps'
+req.estimated_delivery_dates = [date]
+result = sdk.estimated_delivery_date.predict_batch(req)
+print(result)
+```
+
+## Help
+
+If you get stuck, we're here to help:
+
+- [Issue Tracker](https://github.com/AfterShip/tracking-sdk-python/issues) for questions, feature requests, bug reports and general discussion related to this package. Try searching before you create a new issue.
+- Contact AfterShip official support via support@aftership.com
+
+## License
+Copyright (c) 2024 AfterShip
+
+Licensed under the MIT license.
\ No newline at end of file
diff --git a/poetry.lock b/poetry.lock
new file mode 100644
index 0000000..cd682e1
--- /dev/null
+++ b/poetry.lock
@@ -0,0 +1,516 @@
+# This file is automatically @generated by Poetry 1.8.3 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.4.0"
+description = "High level compatibility layer for multiple asynchronous event loop implementations"
+optional = false
+python-versions = ">=3.8"
+files = [
+{file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"},
+{file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"},
+]
+
+[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)", "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", "uvloop (>=0.17)"]
+trio = ["trio (>=0.23)"]
+
+[[package]]
+name = "certifi"
+version = "2024.6.2"
+description = "Python package for providing Mozilla's CA Bundle."
+optional = false
+python-versions = ">=3.6"
+files = [
+{file = "certifi-2024.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"},
+{file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"},
+]
+
+[[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.2.1"
+description = "Backport of PEP 654 (exception groups)"
+optional = false
+python-versions = ">=3.7"
+files = [
+{file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"},
+{file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"},
+]
+
+[package.extras]
+test = ["pytest (>=6)"]
+
+[[package]]
+name = "flake8"
+version = "5.0.4"
+description = "the modular source code checker: pep8 pyflakes and co"
+optional = false
+python-versions = ">=3.6.1"
+files = [
+{file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"},
+{file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"},
+]
+
+[package.dependencies]
+mccabe = ">=0.7.0,<0.8.0"
+pycodestyle = ">=2.9.0,<2.10.0"
+pyflakes = ">=2.5.0,<2.6.0"
+
+[[package]]
+name = "h11"
+version = "0.14.0"
+description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
+optional = false
+python-versions = ">=3.7"
+files = [
+{file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
+{file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
+]
+
+[[package]]
+name = "httpcore"
+version = "1.0.5"
+description = "A minimal low-level HTTP client."
+optional = false
+python-versions = ">=3.8"
+files = [
+{file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"},
+{file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"},
+]
+
+[package.dependencies]
+certifi = "*"
+h11 = ">=0.13,<0.15"
+
+[package.extras]
+asyncio = ["anyio (>=4.0,<5.0)"]
+http2 = ["h2 (>=3,<5)"]
+socks = ["socksio (==1.*)"]
+trio = ["trio (>=0.22.0,<0.26.0)"]
+
+[[package]]
+name = "httpx"
+version = "0.27.0"
+description = "The next generation HTTP client."
+optional = false
+python-versions = ">=3.8"
+files = [
+{file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"},
+{file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"},
+]
+
+[package.dependencies]
+anyio = "*"
+certifi = "*"
+httpcore = "==1.*"
+idna = "*"
+sniffio = "*"
+
+[package.extras]
+brotli = ["brotli", "brotlicffi"]
+cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
+http2 = ["h2 (>=3,<5)"]
+socks = ["socksio (==1.*)"]
+
+[[package]]
+name = "idna"
+version = "3.7"
+description = "Internationalized Domain Names in Applications (IDNA)"
+optional = false
+python-versions = ">=3.5"
+files = [
+{file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
+{file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
+]
+
+[[package]]
+name = "iniconfig"
+version = "2.0.0"
+description = "brain-dead simple config-ini parsing"
+optional = false
+python-versions = ">=3.7"
+files = [
+{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
+{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
+]
+
+[[package]]
+name = "mccabe"
+version = "0.7.0"
+description = "McCabe checker, plugin for flake8"
+optional = false
+python-versions = ">=3.6"
+files = [
+{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
+{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
+]
+
+[[package]]
+name = "packaging"
+version = "24.0"
+description = "Core utilities for Python packages"
+optional = false
+python-versions = ">=3.7"
+files = [
+{file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
+{file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
+]
+
+[[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 = "pycodestyle"
+version = "2.9.1"
+description = "Python style guide checker"
+optional = false
+python-versions = ">=3.6"
+files = [
+{file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"},
+{file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"},
+]
+
+[[package]]
+name = "pycryptodome"
+version = "3.20.0"
+description = "Cryptographic library for Python"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+files = [
+{file = "pycryptodome-3.20.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:f0e6d631bae3f231d3634f91ae4da7a960f7ff87f2865b2d2b831af1dfb04e9a"},
+{file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:baee115a9ba6c5d2709a1e88ffe62b73ecc044852a925dcb67713a288c4ec70f"},
+{file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:417a276aaa9cb3be91f9014e9d18d10e840a7a9b9a9be64a42f553c5b50b4d1d"},
+{file = "pycryptodome-3.20.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a1250b7ea809f752b68e3e6f3fd946b5939a52eaeea18c73bdab53e9ba3c2dd"},
+{file = "pycryptodome-3.20.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:d5954acfe9e00bc83ed9f5cb082ed22c592fbbef86dc48b907238be64ead5c33"},
+{file = "pycryptodome-3.20.0-cp27-cp27m-win32.whl", hash = "sha256:06d6de87c19f967f03b4cf9b34e538ef46e99a337e9a61a77dbe44b2cbcf0690"},
+{file = "pycryptodome-3.20.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ec0bb1188c1d13426039af8ffcb4dbe3aad1d7680c35a62d8eaf2a529b5d3d4f"},
+{file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5601c934c498cd267640b57569e73793cb9a83506f7c73a8ec57a516f5b0b091"},
+{file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d29daa681517f4bc318cd8a23af87e1f2a7bad2fe361e8aa29c77d652a065de4"},
+{file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3427d9e5310af6680678f4cce149f54e0bb4af60101c7f2c16fdf878b39ccccc"},
+{file = "pycryptodome-3.20.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:3cd3ef3aee1079ae44afaeee13393cf68b1058f70576b11439483e34f93cf818"},
+{file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac1c7c0624a862f2e53438a15c9259d1655325fc2ec4392e66dc46cdae24d044"},
+{file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:76658f0d942051d12a9bd08ca1b6b34fd762a8ee4240984f7c06ddfb55eaf15a"},
+{file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f35d6cee81fa145333137009d9c8ba90951d7d77b67c79cbe5f03c7eb74d8fe2"},
+{file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cb39afede7055127e35a444c1c041d2e8d2f1f9c121ecef573757ba4cd2c3c"},
+{file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a4c4dc60b78ec41d2afa392491d788c2e06edf48580fbfb0dd0f828af49d25"},
+{file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fb3b87461fa35afa19c971b0a2b7456a7b1db7b4eba9a8424666104925b78128"},
+{file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:acc2614e2e5346a4a4eab6e199203034924313626f9620b7b4b38e9ad74b7e0c"},
+{file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:210ba1b647837bfc42dd5a813cdecb5b86193ae11a3f5d972b9a0ae2c7e9e4b4"},
+{file = "pycryptodome-3.20.0-cp35-abi3-win32.whl", hash = "sha256:8d6b98d0d83d21fb757a182d52940d028564efe8147baa9ce0f38d057104ae72"},
+{file = "pycryptodome-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:9b3ae153c89a480a0ec402e23db8d8d84a3833b65fa4b15b81b83be9d637aab9"},
+{file = "pycryptodome-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:4401564ebf37dfde45d096974c7a159b52eeabd9969135f0426907db367a652a"},
+{file = "pycryptodome-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:ec1f93feb3bb93380ab0ebf8b859e8e5678c0f010d2d78367cf6bc30bfeb148e"},
+{file = "pycryptodome-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:acae12b9ede49f38eb0ef76fdec2df2e94aad85ae46ec85be3648a57f0a7db04"},
+{file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f47888542a0633baff535a04726948e876bf1ed880fddb7c10a736fa99146ab3"},
+{file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e0e4a987d38cfc2e71b4a1b591bae4891eeabe5fa0f56154f576e26287bfdea"},
+{file = "pycryptodome-3.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c18b381553638414b38705f07d1ef0a7cf301bc78a5f9bc17a957eb19446834b"},
+{file = "pycryptodome-3.20.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a60fedd2b37b4cb11ccb5d0399efe26db9e0dd149016c1cc6c8161974ceac2d6"},
+{file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:405002eafad114a2f9a930f5db65feef7b53c4784495dd8758069b89baf68eab"},
+{file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ab6ab0cb755154ad14e507d1df72de9897e99fd2d4922851a276ccc14f4f1a5"},
+{file = "pycryptodome-3.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:acf6e43fa75aca2d33e93409f2dafe386fe051818ee79ee8a3e21de9caa2ac9e"},
+{file = "pycryptodome-3.20.0.tar.gz", hash = "sha256:09609209ed7de61c2b560cc5c8c4fbf892f8b15b1faf7e4cbffac97db1fffda7"},
+]
+
+[[package]]
+name = "pydantic"
+version = "2.7.3"
+description = "Data validation using Python type hints"
+optional = false
+python-versions = ">=3.8"
+files = [
+{file = "pydantic-2.7.3-py3-none-any.whl", hash = "sha256:ea91b002777bf643bb20dd717c028ec43216b24a6001a280f83877fd2655d0b4"},
+{file = "pydantic-2.7.3.tar.gz", hash = "sha256:c46c76a40bb1296728d7a8b99aa73dd70a48c3510111ff290034f860c99c419e"},
+]
+
+[package.dependencies]
+annotated-types = ">=0.4.0"
+pydantic-core = "2.18.4"
+typing-extensions = ">=4.6.1"
+
+[package.extras]
+email = ["email-validator (>=2.0.0)"]
+
+[[package]]
+name = "pydantic-core"
+version = "2.18.4"
+description = "Core functionality for Pydantic validation and serialization"
+optional = false
+python-versions = ">=3.8"
+files = [
+{file = "pydantic_core-2.18.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4"},
+{file = "pydantic_core-2.18.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26"},
+{file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a"},
+{file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851"},
+{file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d"},
+{file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724"},
+{file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be"},
+{file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb"},
+{file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c"},
+{file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e"},
+{file = "pydantic_core-2.18.4-cp310-none-win32.whl", hash = "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc"},
+{file = "pydantic_core-2.18.4-cp310-none-win_amd64.whl", hash = "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0"},
+{file = "pydantic_core-2.18.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d"},
+{file = "pydantic_core-2.18.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4"},
+{file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4"},
+{file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2"},
+{file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd"},
+{file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d"},
+{file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8"},
+{file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951"},
+{file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2"},
+{file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9"},
+{file = "pydantic_core-2.18.4-cp311-none-win32.whl", hash = "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558"},
+{file = "pydantic_core-2.18.4-cp311-none-win_amd64.whl", hash = "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b"},
+{file = "pydantic_core-2.18.4-cp311-none-win_arm64.whl", hash = "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805"},
+{file = "pydantic_core-2.18.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2"},
+{file = "pydantic_core-2.18.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef"},
+{file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695"},
+{file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34"},
+{file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1"},
+{file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077"},
+{file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3"},
+{file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9"},
+{file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c"},
+{file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8"},
+{file = "pydantic_core-2.18.4-cp312-none-win32.whl", hash = "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07"},
+{file = "pydantic_core-2.18.4-cp312-none-win_amd64.whl", hash = "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a"},
+{file = "pydantic_core-2.18.4-cp312-none-win_arm64.whl", hash = "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f"},
+{file = "pydantic_core-2.18.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2"},
+{file = "pydantic_core-2.18.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae"},
+{file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a"},
+{file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc"},
+{file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78"},
+{file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8"},
+{file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d"},
+{file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057"},
+{file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b"},
+{file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af"},
+{file = "pydantic_core-2.18.4-cp38-none-win32.whl", hash = "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2"},
+{file = "pydantic_core-2.18.4-cp38-none-win_amd64.whl", hash = "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443"},
+{file = "pydantic_core-2.18.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528"},
+{file = "pydantic_core-2.18.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223"},
+{file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f"},
+{file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154"},
+{file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30"},
+{file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443"},
+{file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94"},
+{file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23"},
+{file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b"},
+{file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a"},
+{file = "pydantic_core-2.18.4-cp39-none-win32.whl", hash = "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d"},
+{file = "pydantic_core-2.18.4-cp39-none-win_amd64.whl", hash = "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e"},
+{file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d"},
+{file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab"},
+{file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4"},
+{file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc"},
+{file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507"},
+{file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef"},
+{file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d"},
+{file = "pydantic_core-2.18.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5"},
+{file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312"},
+{file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5"},
+{file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e"},
+{file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8"},
+{file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f"},
+{file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce"},
+{file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee"},
+{file = "pydantic_core-2.18.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9"},
+{file = "pydantic_core-2.18.4.tar.gz", hash = "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864"},
+]
+
+[package.dependencies]
+typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
+
+[[package]]
+name = "pyflakes"
+version = "2.5.0"
+description = "passive checker of Python programs"
+optional = false
+python-versions = ">=3.6"
+files = [
+{file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"},
+{file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"},
+]
+
+[[package]]
+name = "pytest"
+version = "8.2.2"
+description = "pytest: simple powerful testing with Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+{file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"},
+{file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
+exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
+iniconfig = "*"
+packaging = "*"
+pluggy = ">=1.5,<2.0"
+tomli = {version = ">=1", markers = "python_version < \"3.11\""}
+
+[package.extras]
+dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
+
+[[package]]
+name = "retrying"
+version = "1.3.4"
+description = "Retrying"
+optional = false
+python-versions = "*"
+files = [
+{file = "retrying-1.3.4-py3-none-any.whl", hash = "sha256:8cc4d43cb8e1125e0ff3344e9de678fefd85db3b750b81b2240dc0183af37b35"},
+{file = "retrying-1.3.4.tar.gz", hash = "sha256:345da8c5765bd982b1d1915deb9102fd3d1f7ad16bd84a9700b85f64d24e8f3e"},
+]
+
+[package.dependencies]
+six = ">=1.7.0"
+
+[[package]]
+name = "ruff"
+version = "0.4.8"
+description = "An extremely fast Python linter and code formatter, written in Rust."
+optional = false
+python-versions = ">=3.7"
+files = [
+{file = "ruff-0.4.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7663a6d78f6adb0eab270fa9cf1ff2d28618ca3a652b60f2a234d92b9ec89066"},
+{file = "ruff-0.4.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eeceb78da8afb6de0ddada93112869852d04f1cd0f6b80fe464fd4e35c330913"},
+{file = "ruff-0.4.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aad360893e92486662ef3be0a339c5ca3c1b109e0134fcd37d534d4be9fb8de3"},
+{file = "ruff-0.4.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:284c2e3f3396fb05f5f803c9fffb53ebbe09a3ebe7dda2929ed8d73ded736deb"},
+{file = "ruff-0.4.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7354f921e3fbe04d2a62d46707e569f9315e1a613307f7311a935743c51a764"},
+{file = "ruff-0.4.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:72584676164e15a68a15778fd1b17c28a519e7a0622161eb2debdcdabdc71883"},
+{file = "ruff-0.4.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9678d5c9b43315f323af2233a04d747409d1e3aa6789620083a82d1066a35199"},
+{file = "ruff-0.4.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704977a658131651a22b5ebeb28b717ef42ac6ee3b11e91dc87b633b5d83142b"},
+{file = "ruff-0.4.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05f8d6f0c3cce5026cecd83b7a143dcad503045857bc49662f736437380ad45"},
+{file = "ruff-0.4.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6ea874950daca5697309d976c9afba830d3bf0ed66887481d6bca1673fc5b66a"},
+{file = "ruff-0.4.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:fc95aac2943ddf360376be9aa3107c8cf9640083940a8c5bd824be692d2216dc"},
+{file = "ruff-0.4.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:384154a1c3f4bf537bac69f33720957ee49ac8d484bfc91720cc94172026ceed"},
+{file = "ruff-0.4.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e9d5ce97cacc99878aa0d084c626a15cd21e6b3d53fd6f9112b7fc485918e1fa"},
+{file = "ruff-0.4.8-py3-none-win32.whl", hash = "sha256:6d795d7639212c2dfd01991259460101c22aabf420d9b943f153ab9d9706e6a9"},
+{file = "ruff-0.4.8-py3-none-win_amd64.whl", hash = "sha256:e14a3a095d07560a9d6769a72f781d73259655919d9b396c650fc98a8157555d"},
+{file = "ruff-0.4.8-py3-none-win_arm64.whl", hash = "sha256:14019a06dbe29b608f6b7cbcec300e3170a8d86efaddb7b23405cb7f7dcaf780"},
+{file = "ruff-0.4.8.tar.gz", hash = "sha256:16d717b1d57b2e2fd68bd0bf80fb43931b79d05a7131aa477d66fc40fbd86268"},
+]
+
+[[package]]
+name = "six"
+version = "1.16.0"
+description = "Python 2 and 3 compatibility utilities"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+files = [
+{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
+{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
+]
+
+[[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 = "socksio"
+version = "1.0.0"
+description = "Sans-I/O implementation of SOCKS4, SOCKS4A, and SOCKS5."
+optional = false
+python-versions = ">=3.6"
+files = [
+{file = "socksio-1.0.0-py3-none-any.whl", hash = "sha256:95dc1f15f9b34e8d7b16f06d74b8ccf48f609af32ab33c608d08761c5dcbb1f3"},
+{file = "socksio-1.0.0.tar.gz", hash = "sha256:f88beb3da5b5c38b9890469de67d0cb0f9d494b78b106ca1845f96c10b91c4ac"},
+]
+
+[[package]]
+name = "tomli"
+version = "2.0.1"
+description = "A lil' TOML parser"
+optional = false
+python-versions = ">=3.7"
+files = [
+{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
+{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.12.1"
+description = "Backported and Experimental Type Hints for Python 3.8+"
+optional = false
+python-versions = ">=3.8"
+files = [
+{file = "typing_extensions-4.12.1-py3-none-any.whl", hash = "sha256:6024b58b69089e5a89c347397254e35f1bf02a907728ec7fee9bf0fe837d203a"},
+{file = "typing_extensions-4.12.1.tar.gz", hash = "sha256:915f5e35ff76f56588223f15fdd5938f9a1cf9195c0de25130c627e4d597f6d1"},
+]
+
+[[package]]
+name = "urllib3"
+version = "2.2.2"
+description = "HTTP library with thread-safe connection pooling, file post, and more."
+optional = false
+python-versions = ">=3.8"
+files = [
+{file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"},
+{file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"},
+]
+
+[package.extras]
+brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
+h2 = ["h2 (>=4,<5)"]
+socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
+zstd = ["zstandard (>=0.18.0)"]
+
+[metadata]
+lock-version = "2.0"
+python-versions = "^3.8"
+content-hash = "f4dc3863a79f37180dffdb48dfb2dba5a54de46477a55b21b3346051dfc9188a"
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..6cf6c08
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,54 @@
+[tool.poetry]
+name = "tracking-sdk"
+version = "2.0.0"
+description = "python SDK for AfterShip Tracking"
+authors = ["AfterShip "]
+license = "MIT"
+readme = "README.md"
+keywords = ["aftership", "api", "logistics"]
+
+classifiers = [
+ "Development Status :: 5 - Production/Stable",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: MIT License",
+ "Topic :: Software Development",
+ "Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+]
+
+packages = [
+ { include = "tracking" },
+ { include = "*.md" },
+ { include = "LICENSE" },
+]
+
+[tool.poetry.dependencies]
+python = "^3.8"
+pycryptodome = ">= 3.9.0"
+pydantic = ">=2"
+httpx = ">=0.27.0"
+retrying = "^1.3.4"
+typing_extensions = ">=4.7.1"
+urllib3 = "^2.2.2"
+socksio = "^1.0.0"
+
+[tool.poetry.group.dev.dependencies]
+ruff = "^0.4.8"
+pytest = "^8.2.2"
+flake8 = ">=4.0.0"
+
+[build-system]
+requires = ["poetry>=0.12"]
+build-backend = "poetry.masonry.api"
+
+[tool.ruff]
+target-version = "py311"
+line-length = 100
+
+[tool.ruff.format]
+quote-style = "double"
+indent-style = "space"
+docstring-code-format = true
+docstring-code-line-length = 60
\ No newline at end of file
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..5972212
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,8 @@
+[flake8]
+max-line-length=99
+
+[bdist_wheel]
+universal = 0
+
+[metadata]
+license_file = LICENSE
\ No newline at end of file
diff --git a/tracking/__init__.py b/tracking/__init__.py
new file mode 100644
index 0000000..6f6454a
--- /dev/null
+++ b/tracking/__init__.py
@@ -0,0 +1,14 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+# flake8: noqa
+
+from .auth import ApiKey, Aes, Rsa
+from .client import Client
+from .configuration import Configuration
+from . import exceptions
+from .models import *
+
+__version__ = "2.0.0"
diff --git a/tracking/api/__init__.py b/tracking/api/__init__.py
new file mode 100644
index 0000000..bfc26b3
--- /dev/null
+++ b/tracking/api/__init__.py
@@ -0,0 +1,18 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+__all__ = [
+ "CourierApi",
+ "EstimatedDeliveryDateApi",
+ "LastCheckpointApi",
+ "NotificationApi",
+ "TrackingApi",
+]
+
+from .courier import CourierApi
+from .estimated_delivery_date import EstimatedDeliveryDateApi
+from .last_checkpoint import LastCheckpointApi
+from .notification import NotificationApi
+from .tracking import TrackingApi
diff --git a/tracking/api/courier.py b/tracking/api/courier.py
new file mode 100644
index 0000000..a893811
--- /dev/null
+++ b/tracking/api/courier.py
@@ -0,0 +1,79 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+import json
+from typing import Union
+
+
+from tracking.models import (
+ TrackingDetectCourierRequest,
+ DetectCourierResponse,
+ GetAllCouriersResponse,
+ GetUserCouriersResponse,
+)
+from tracking.request import ApiClient, validate_params
+
+
+class CourierApi(ApiClient):
+ """CourierApi api implements"""
+
+ @validate_params
+ def detect_courier(
+ self, detect_courier_request: Union[TrackingDetectCourierRequest, dict], **kwargs
+ ) -> DetectCourierResponse:
+ """
+ Return a list of matched couriers based on tracking number format and or a list of couriers.
+ :param detect_courier_request:
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ """
+ url = "/tracking/2024-04/couriers/detect"
+
+ body = detect_courier_request
+ if not isinstance(body, dict):
+ body = detect_courier_request.model_dump(exclude_none=True)
+ body = json.dumps({"tracking": body})
+
+ result = self._request("POST", url=url, body=body, **kwargs)
+ return DetectCourierResponse().from_dict(result)
+
+ @validate_params
+ def get_all_couriers(self, **kwargs) -> GetAllCouriersResponse:
+ """
+ Return a list of all couriers.
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ """
+ url = "/tracking/2024-04/couriers/all"
+
+ result = self._request("GET", url=url, **kwargs)
+ return GetAllCouriersResponse().from_dict(result)
+
+ @validate_params
+ def get_user_couriers(self, **kwargs) -> GetUserCouriersResponse:
+ """
+ Return a list of .
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ """
+ url = "/tracking/2024-04/couriers"
+
+ result = self._request("GET", url=url, **kwargs)
+ return GetUserCouriersResponse().from_dict(result)
diff --git a/tracking/api/estimated_delivery_date.py b/tracking/api/estimated_delivery_date.py
new file mode 100644
index 0000000..3e693e6
--- /dev/null
+++ b/tracking/api/estimated_delivery_date.py
@@ -0,0 +1,43 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+import json
+from typing import Union
+
+
+from tracking.models import (
+ PredictBatchRequest,
+ PredictBatchResponse,
+)
+from tracking.request import ApiClient, validate_params
+
+
+class EstimatedDeliveryDateApi(ApiClient):
+ """EstimatedDeliveryDateApi api implements"""
+
+ @validate_params
+ def predict_batch(
+ self, predict_batch_request: Union[PredictBatchRequest, dict], **kwargs
+ ) -> PredictBatchResponse:
+ """
+ > The estimated delivery date is provided by AfterShip, based on its AI-predictive model. You can display the EDD on the product page, cart, and order checkout page. It indicates when a customer will receive the order.You can use to activate this feature.Supported functionalities require:1. One `EstimatedDeliveryDate` object for one prediction result.2. Maximum 5 `EstimatedDeliveryDate` objects are allowed.3. API call will fail if any of the requests `EstimatedDeliveryDate` objects do not meet the specification requirement.
+ :param predict_batch_request:
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ """
+ url = "/tracking/2024-04/estimated-delivery-date/predict-batch"
+
+ body = predict_batch_request
+ if not isinstance(body, dict):
+ body = predict_batch_request.model_dump(exclude_none=True)
+ body = json.dumps(body)
+
+ result = self._request("POST", url=url, body=body, **kwargs)
+ return PredictBatchResponse().from_dict(result)
diff --git a/tracking/api/last_checkpoint.py b/tracking/api/last_checkpoint.py
new file mode 100644
index 0000000..05a16f4
--- /dev/null
+++ b/tracking/api/last_checkpoint.py
@@ -0,0 +1,92 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from typing import Annotated
+
+from pydantic import Field
+
+from tracking.models import (
+ GetCheckpointBySlugTrackingNumberResponse,
+ GetCheckpointByTrackingIdResponse,
+)
+from tracking.request import ApiClient, validate_params
+
+
+class LastCheckpointApi(ApiClient):
+ """LastCheckpointApi api implements"""
+
+ @validate_params
+ def get_checkpoint_by_slug_tracking_number(
+ self,
+ slug: Annotated[str, Field(min_length=1)],
+ tracking_number: Annotated[str, Field(min_length=1)],
+ **kwargs,
+ ) -> GetCheckpointBySlugTrackingNumberResponse:
+ """
+ Return the tracking information of the last checkpoint of a single tracking.
+ :param slug: str. Tracking slug.
+ :param tracking_number: str. Tracking number.
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ query params:
+ **fields**: str. List of fields to include in the response. Use comma for multiple values. Fields to include: `slug`, `created_at`, `checkpoint_time`, `city`, `coordinates`, `country_iso3`, `country_name`, `message`, `state`, `tag`, `zip`
+ **lang**: str. Support Chinese to English translation for `china-ems` and `china-post` only
+ **tracking_account_number**: str. Additional field required by some carriers to retrieve the tracking info. The shipper’s carrier account number. Refer to our article on for more details.
+ **tracking_origin_country**: str. Additional field required by some carriers to retrieve the tracking info. The origin country/region of the shipment. Refer to our article on for more details.
+ **tracking_destination_country**: str. Additional field required by some carriers to retrieve the tracking info. The destination country/region of the shipment. Refer to our article on for more details.
+ **tracking_key**: str. Additional field required by some carriers to retrieve the tracking info. A type of tracking credential required by some carriers. Refer to our article on for more details.
+ **tracking_postal_code**: str. Additional field required by some carriers to retrieve the tracking info. The postal code of the recipient’s address. Refer to our article on for more details.
+ **tracking_ship_date**: str. Additional field required by some carriers to retrieve the tracking info. The date the shipment was sent, using the format YYYYMMDD. Refer to our article on for more details.
+ **tracking_state**: str. Additional field required by some carriers to retrieve the tracking info. The state/province of the recipient’s address. Refer to our article on for more details.
+ """
+ url = f"/tracking/2024-04/last_checkpoint/{slug}/{tracking_number}"
+ params_keys = {
+ "fields",
+ "lang",
+ "tracking_account_number",
+ "tracking_origin_country",
+ "tracking_destination_country",
+ "tracking_key",
+ "tracking_postal_code",
+ "tracking_ship_date",
+ "tracking_state",
+ }
+ params = {key: kwargs.pop(key) for key in params_keys if key in kwargs}
+
+ result = self._request("GET", url=url, params=params, **kwargs)
+ return GetCheckpointBySlugTrackingNumberResponse().from_dict(result)
+
+ @validate_params
+ def get_checkpoint_by_tracking_id(
+ self, tracking_id: Annotated[str, Field(min_length=1)], **kwargs
+ ) -> GetCheckpointByTrackingIdResponse:
+ """
+ Return the tracking information of the last checkpoint of a single tracking.
+ :param tracking_id: str. tracking id.
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ query params:
+ **fields**: str. List of fields to include in the response. Use comma for multiple values. Fields to include: `slug`, `created_at`, `checkpoint_time`, `city`, `coordinates`, `country_iso3`, `country_name`, `message`, `state`, `tag`, `zip`
+ **lang**: str. Support Chinese to English translation for `china-ems` and `china-post` only
+ """
+ url = f"/tracking/2024-04/last_checkpoint/{tracking_id}"
+ params_keys = {
+ "fields",
+ "lang",
+ }
+ params = {key: kwargs.pop(key) for key in params_keys if key in kwargs}
+
+ result = self._request("GET", url=url, params=params, **kwargs)
+ return GetCheckpointByTrackingIdResponse().from_dict(result)
diff --git a/tracking/api/notification.py b/tracking/api/notification.py
new file mode 100644
index 0000000..051fa96
--- /dev/null
+++ b/tracking/api/notification.py
@@ -0,0 +1,237 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+import json
+from typing import Union, Annotated
+
+from pydantic import Field
+
+from tracking.models import (
+ NotificationRequestV1,
+ Notification,
+)
+from tracking.request import ApiClient, validate_params
+
+
+class NotificationApi(ApiClient):
+ """NotificationApi api implements"""
+
+ @validate_params
+ def add_notification_by_slug_tracking_number(
+ self,
+ slug: Annotated[str, Field(min_length=1)],
+ tracking_number: Annotated[str, Field(min_length=1)],
+ add_notification_by_slug_tracking_number_request: Union[NotificationRequestV1, dict],
+ **kwargs,
+ ) -> Notification:
+ """
+ Add notification receivers to a tracking number.
+ :param slug: str. Tracking slug.
+ :param tracking_number: str. Tracking number.
+ :param add_notification_by_slug_tracking_number_request:
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ query params:
+ **tracking_account_number**: str. Additional field required by some carriers to retrieve the tracking info. The shipper’s carrier account number. Refer to our article on for more details.
+ **tracking_origin_country**: str. Additional field required by some carriers to retrieve the tracking info. The origin country/region of the shipment. Refer to our article on for more details.
+ **tracking_destination_country**: str. Additional field required by some carriers to retrieve the tracking info. The destination country/region of the shipment. Refer to our article on for more details.
+ **tracking_key**: str. Additional field required by some carriers to retrieve the tracking info. A type of tracking credential required by some carriers. Refer to our article on for more details.
+ **tracking_postal_code**: str. Additional field required by some carriers to retrieve the tracking info. The postal code of the recipient’s address. Refer to our article on for more details.
+ **tracking_ship_date**: str. Additional field required by some carriers to retrieve the tracking info. The date the shipment was sent, using the format YYYYMMDD. Refer to our article on for more details.
+ **tracking_state**: str. Additional field required by some carriers to retrieve the tracking info. The state/province of the recipient’s address. Refer to our article on for more details.
+ """
+ url = f"/tracking/2024-04/notifications/{slug}/{tracking_number}/add"
+ params_keys = {
+ "tracking_account_number",
+ "tracking_origin_country",
+ "tracking_destination_country",
+ "tracking_key",
+ "tracking_postal_code",
+ "tracking_ship_date",
+ "tracking_state",
+ }
+ params = {key: kwargs.pop(key) for key in params_keys if key in kwargs}
+
+ body = add_notification_by_slug_tracking_number_request
+ if not isinstance(body, dict):
+ body = add_notification_by_slug_tracking_number_request.model_dump(exclude_none=True)
+ body = json.dumps({"notification": body})
+
+ result = self._request("POST", url=url, params=params, body=body, **kwargs)
+ return Notification().from_dict(result.get("notification"))
+
+ @validate_params
+ def add_notification_by_tracking_id(
+ self,
+ tracking_id: Annotated[str, Field(min_length=1)],
+ add_notification_by_tracking_id_request: Union[NotificationRequestV1, dict],
+ **kwargs,
+ ) -> Notification:
+ """
+ Add notification receivers to a tracking number.
+ :param tracking_id: str. tracking id.
+ :param add_notification_by_tracking_id_request:
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ """
+ url = f"/tracking/2024-04/notifications/{tracking_id}/add"
+
+ body = add_notification_by_tracking_id_request
+ if not isinstance(body, dict):
+ body = add_notification_by_tracking_id_request.model_dump(exclude_none=True)
+ body = json.dumps({"notification": body})
+
+ result = self._request("POST", url=url, body=body, **kwargs)
+ return Notification().from_dict(result.get("notification"))
+
+ @validate_params
+ def delete_notification_by_slug_tracking_number(
+ self,
+ slug: Annotated[str, Field(min_length=1)],
+ tracking_number: Annotated[str, Field(min_length=1)],
+ delete_notification_by_slug_tracking_number_request: Union[NotificationRequestV1, dict],
+ **kwargs,
+ ) -> Notification:
+ """
+ Remove notification receivers from a tracking number.
+ :param slug: str. Tracking slug.
+ :param tracking_number: str. Tracking number.
+ :param delete_notification_by_slug_tracking_number_request:
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ query params:
+ **tracking_account_number**: str. Additional field required by some carriers to retrieve the tracking info. The shipper’s carrier account number. Refer to our article on for more details.
+ **tracking_origin_country**: str. Additional field required by some carriers to retrieve the tracking info. The origin country/region of the shipment. Refer to our article on for more details.
+ **tracking_destination_country**: str. Additional field required by some carriers to retrieve the tracking info. The destination country/region of the shipment. Refer to our article on for more details.
+ **tracking_key**: str. Additional field required by some carriers to retrieve the tracking info. A type of tracking credential required by some carriers. Refer to our article on for more details.
+ **tracking_postal_code**: str. Additional field required by some carriers to retrieve the tracking info. The postal code of the recipient’s address. Refer to our article on for more details.
+ **tracking_ship_date**: str. Additional field required by some carriers to retrieve the tracking info. The date the shipment was sent, using the format YYYYMMDD. Refer to our article on for more details.
+ **tracking_state**: str. Additional field required by some carriers to retrieve the tracking info. The state/province of the recipient’s address. Refer to our article on for more details.
+ """
+ url = f"/tracking/2024-04/notifications/{slug}/{tracking_number}/remove"
+ params_keys = {
+ "tracking_account_number",
+ "tracking_origin_country",
+ "tracking_destination_country",
+ "tracking_key",
+ "tracking_postal_code",
+ "tracking_ship_date",
+ "tracking_state",
+ }
+ params = {key: kwargs.pop(key) for key in params_keys if key in kwargs}
+
+ body = delete_notification_by_slug_tracking_number_request
+ if not isinstance(body, dict):
+ body = delete_notification_by_slug_tracking_number_request.model_dump(exclude_none=True)
+ body = json.dumps({"notification": body})
+
+ result = self._request("POST", url=url, params=params, body=body, **kwargs)
+ return Notification().from_dict(result.get("notification"))
+
+ @validate_params
+ def delete_notification_by_tracking_id(
+ self,
+ tracking_id: Annotated[str, Field(min_length=1)],
+ delete_notification_by_tracking_id_request: Union[NotificationRequestV1, dict],
+ **kwargs,
+ ) -> Notification:
+ """
+ Remove notification receivers from a tracking number.
+ :param tracking_id: str. tracking id.
+ :param delete_notification_by_tracking_id_request:
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ """
+ url = f"/tracking/2024-04/notifications/{tracking_id}/remove"
+
+ body = delete_notification_by_tracking_id_request
+ if not isinstance(body, dict):
+ body = delete_notification_by_tracking_id_request.model_dump(exclude_none=True)
+ body = json.dumps({"notification": body})
+
+ result = self._request("POST", url=url, body=body, **kwargs)
+ return Notification().from_dict(result.get("notification"))
+
+ @validate_params
+ def get_notification_by_slug_tracking_number(
+ self,
+ slug: Annotated[str, Field(min_length=1)],
+ tracking_number: Annotated[str, Field(min_length=1)],
+ **kwargs,
+ ) -> Notification:
+ """
+ Get contact information for the users to notify when the tracking changes. Please note that only customer receivers will be returned. Any `email`, `sms` or `webhook` that belongs to the Store will not be returned.
+ :param slug: str. Tracking slug.
+ :param tracking_number: str. Tracking number.
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ query params:
+ **tracking_account_number**: str. Additional field required by some carriers to retrieve the tracking info. The shipper’s carrier account number. Refer to our article on for more details.
+ **tracking_origin_country**: str. Additional field required by some carriers to retrieve the tracking info. The origin country/region of the shipment. Refer to our article on for more details.
+ **tracking_destination_country**: str. Additional field required by some carriers to retrieve the tracking info. The destination country/region of the shipment. Refer to our article on for more details.
+ **tracking_key**: str. Additional field required by some carriers to retrieve the tracking info. A type of tracking credential required by some carriers. Refer to our article on for more details.
+ **tracking_postal_code**: str. Additional field required by some carriers to retrieve the tracking info. The postal code of the recipient’s address. Refer to our article on for more details.
+ **tracking_ship_date**: str. Additional field required by some carriers to retrieve the tracking info. The date the shipment was sent, using the format YYYYMMDD. Refer to our article on for more details.
+ **tracking_state**: str. Additional field required by some carriers to retrieve the tracking info. The state/province of the recipient’s address. Refer to our article on for more details.
+ """
+ url = f"/tracking/2024-04/notifications/{slug}/{tracking_number}"
+ params_keys = {
+ "tracking_account_number",
+ "tracking_origin_country",
+ "tracking_destination_country",
+ "tracking_key",
+ "tracking_postal_code",
+ "tracking_ship_date",
+ "tracking_state",
+ }
+ params = {key: kwargs.pop(key) for key in params_keys if key in kwargs}
+
+ result = self._request("GET", url=url, params=params, **kwargs)
+ return Notification().from_dict(result.get("notification"))
+
+ @validate_params
+ def get_notification_by_tracking_id(
+ self, tracking_id: Annotated[str, Field(min_length=1)], **kwargs
+ ) -> Notification:
+ """
+ Get contact information for the users to notify when the tracking changes. Please note that only customer receivers will be returned. Any `email`, `sms` or `webhook` that belongs to the Store will not be returned.
+ :param tracking_id: str. tracking id.
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ """
+ url = f"/tracking/2024-04/notifications/{tracking_id}"
+
+ result = self._request("GET", url=url, **kwargs)
+ return Notification().from_dict(result.get("notification"))
diff --git a/tracking/api/tracking.py b/tracking/api/tracking.py
new file mode 100644
index 0000000..eb4f88d
--- /dev/null
+++ b/tracking/api/tracking.py
@@ -0,0 +1,477 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+import json
+from typing import Union, Annotated
+
+from pydantic import Field
+
+from tracking.models import (
+ TrackingCreateTrackingRequest,
+ Tracking,
+ PartialDeleteTracking,
+ GetTrackingsResponse,
+ MarkTrackingCompletedByIdRequest,
+ MarkTrackingCompletedBySlugTrackingNumberRequest,
+ PartialUpdateTracking,
+ TrackingUpdateTrackingByIdRequest,
+ TrackingUpdateTrackingBySlugTrackingNumberRequest,
+)
+from tracking.request import ApiClient, validate_params
+
+
+class TrackingApi(ApiClient):
+ """TrackingApi api implements"""
+
+ @validate_params
+ def create_tracking(
+ self, create_tracking_request: Union[TrackingCreateTrackingRequest, dict], **kwargs
+ ) -> Tracking:
+ """
+ Create a tracking.
+ :param create_tracking_request:
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ """
+ url = "/tracking/2024-04/trackings"
+
+ body = create_tracking_request
+ if not isinstance(body, dict):
+ body = create_tracking_request.model_dump(exclude_none=True)
+ body = json.dumps({"tracking": body})
+
+ result = self._request("POST", url=url, body=body, **kwargs)
+ return Tracking().from_dict(result.get("tracking"))
+
+ @validate_params
+ def delete_tracking_by_id(
+ self, tracking_id: Annotated[str, Field(min_length=1)], **kwargs
+ ) -> PartialDeleteTracking:
+ """
+ Delete a tracking.
+ :param tracking_id: str. tracking ID.
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ """
+ url = f"/tracking/2024-04/trackings/{tracking_id}"
+
+ result = self._request("DELETE", url=url, **kwargs)
+ return PartialDeleteTracking().from_dict(result.get("tracking"))
+
+ @validate_params
+ def delete_tracking_by_slug_tracking_number(
+ self,
+ slug: Annotated[str, Field(min_length=1)],
+ tracking_number: Annotated[str, Field(min_length=1)],
+ **kwargs,
+ ) -> PartialDeleteTracking:
+ """
+ Delete a tracking.
+ :param slug: str. Tracking slug.
+ :param tracking_number: str. Tracking number.
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ query params:
+ **tracking_account_number**: str. Additional field required by some carriers to retrieve the tracking info. The shipper’s carrier account number. Refer to our article on for more details.
+ **tracking_origin_country**: str. Additional field required by some carriers to retrieve the tracking info. The origin country/region of the shipment. Refer to our article on for more details.
+ **tracking_destination_country**: str. Additional field required by some carriers to retrieve the tracking info. The destination country/region of the shipment. Refer to our article on for more details.
+ **tracking_key**: str. Additional field required by some carriers to retrieve the tracking info. A type of tracking credential required by some carriers. Refer to our article on for more details.
+ **tracking_postal_code**: str. Additional field required by some carriers to retrieve the tracking info. The postal code of the recipient’s address. Refer to our article on for more details.
+ **tracking_ship_date**: str. Additional field required by some carriers to retrieve the tracking info. The date the shipment was sent, using the format YYYYMMDD. Refer to our article on for more details.
+ **tracking_state**: str. Additional field required by some carriers to retrieve the tracking info. The state/province of the recipient’s address. Refer to our article on for more details.
+ """
+ url = f"/tracking/2024-04/trackings/{slug}/{tracking_number}"
+ params_keys = {
+ "tracking_account_number",
+ "tracking_origin_country",
+ "tracking_destination_country",
+ "tracking_key",
+ "tracking_postal_code",
+ "tracking_ship_date",
+ "tracking_state",
+ }
+ params = {key: kwargs.pop(key) for key in params_keys if key in kwargs}
+
+ result = self._request("DELETE", url=url, params=params, **kwargs)
+ return PartialDeleteTracking().from_dict(result.get("tracking"))
+
+ @validate_params
+ def get_tracking_by_id(
+ self, tracking_id: Annotated[str, Field(min_length=1)], **kwargs
+ ) -> Tracking:
+ """
+ Get tracking results of a single tracking.
+ :param tracking_id: str. tracking ID.
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ query params:
+ **fields**: str. List of fields to include in the response. Use comma for multiple values. Fields to include: `tracking_postal_code`, `tracking_ship_date`, `tracking_account_number`, `tracking_key`, `tracking_origin_country`, `tracking_destination_country`, `tracking_state`, `title`, `order_id`, `tag`, `checkpoints`
+ **lang**: str. Translate checkpoint messages from the carrier’s provided language to the target language. Supported target languages include: - English (en) - French (fr) - French Canadian (fr-CA) - Arabic (ar) - Bulgarian (bg) - Catalan (ca) - Croatian (hr) - Czech (cs) - Danish (da) - Dutch (nl) - Estonian (et) - Filipino (tl) - Finnish (fi) - German (de) - Greek (el) - Hebrew (he) - Hindi (hi) - Hungarian (hu) - Indonesian (id) - Italian (it) - Japanese (ja) - Korean (ko) - Latvian (lv) - Lithuanian (lt) - Malay (ms) - Polish (pl) - Portuguese (pt) - Romanian (ro) - Russian (ru) - Serbian (sr) - Slovak (sk) - Slovenian (sl) - Spanish (es) - Swedish (sv) - Thai (th) - Turkish (tr) - Ukrainian (uk) - Vietnamese (vi) - Simplified Chinese (zh-Hans) - Traditional Chinese (zh-Hant) - Norwegian (nb)
+ """
+ url = f"/tracking/2024-04/trackings/{tracking_id}"
+ params_keys = {
+ "fields",
+ "lang",
+ }
+ params = {key: kwargs.pop(key) for key in params_keys if key in kwargs}
+
+ result = self._request("GET", url=url, params=params, **kwargs)
+ return Tracking().from_dict(result.get("tracking"))
+
+ @validate_params
+ def get_tracking_by_slug_tracking_number(
+ self,
+ slug: Annotated[str, Field(min_length=1)],
+ tracking_number: Annotated[str, Field(min_length=1)],
+ **kwargs,
+ ) -> Tracking:
+ """
+ Get tracking results of a single tracking.
+ :param slug: str. Tracking slug.
+ :param tracking_number: str. Tracking number.
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ query params:
+ **fields**: str. List of fields to include in the response. Use comma for multiple values. Fields to include: `tracking_postal_code`, `tracking_ship_date`, `tracking_account_number`, `tracking_key`, `tracking_origin_country`, `tracking_destination_country`, `tracking_state`, `title`, `order_id`, `tag`, `checkpoints`
+ **lang**: str. Support Chinese to English translation for `china-ems` and `china-post` only
+ **tracking_account_number**: str. Additional field required by some carriers to retrieve the tracking info. The shipper’s carrier account number. Refer to our article on for more details.
+ **tracking_origin_country**: str. Additional field required by some carriers to retrieve the tracking info. The origin country/region of the shipment. Refer to our article on for more details.
+ **tracking_destination_country**: str. Additional field required by some carriers to retrieve the tracking info. The destination country/region of the shipment. Refer to our article on for more details.
+ **tracking_key**: str. Additional field required by some carriers to retrieve the tracking info. A type of tracking credential required by some carriers. Refer to our article on for more details.
+ **tracking_postal_code**: str. Additional field required by some carriers to retrieve the tracking info. The postal code of the recipient’s address. Refer to our article on for more details.
+ **tracking_ship_date**: str. Additional field required by some carriers to retrieve the tracking info. The date the shipment was sent, using the format YYYYMMDD. Refer to our article on for more details.
+ **tracking_state**: str. Additional field required by some carriers to retrieve the tracking info. The state/province of the recipient’s address. Refer to our article on for more details.
+ """
+ url = f"/tracking/2024-04/trackings/{slug}/{tracking_number}"
+ params_keys = {
+ "fields",
+ "lang",
+ "tracking_account_number",
+ "tracking_origin_country",
+ "tracking_destination_country",
+ "tracking_key",
+ "tracking_postal_code",
+ "tracking_ship_date",
+ "tracking_state",
+ }
+ params = {key: kwargs.pop(key) for key in params_keys if key in kwargs}
+
+ result = self._request("GET", url=url, params=params, **kwargs)
+ return Tracking().from_dict(result.get("tracking"))
+
+ @validate_params
+ def get_trackings(self, **kwargs) -> GetTrackingsResponse:
+ """
+ Get tracking results of multiple trackings.
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ query params:
+ **page**: int. The page to query. Maximum page number is bounded by total number of queried trackings which cannot exceed 160,000 trackings. (Default: 1)
+ **limit**: int. Number of trackings each page contain. (Default: 100, Max: 200)
+ **keyword**: str. Search the content of the tracking record fields: `tracking_number`, `title`, `order_id`, `customer_name`, `custom_fields`, `emails`, `smses`
+ **tracking_numbers**: str. Tracking number of shipments. Use comma to separate multiple values (Example: RA123456789US,LE123456789US). Supports up to 50 tracking numbers.
+ **slug**: str. Unique courier code Use comma for multiple values. (Example: dhl,ups,usps)
+ **transit_time**: int. Total delivery time in days.- When the shipment is delivered: Transit time = Delivered date - Picked up date- When the shipment is not delivered: Transit time = Current date - Picked up dateValue as `null` for the shipment without pickup date.
+ **origin**: str. Origin country/region of trackings. Use ISO Alpha-3 (three letters). Use comma for multiple values. (Example: USA,HKG)
+ **destination**: str. Destination country/region of trackings. Use ISO Alpha-3 (three letters). Use comma for multiple values. (Example: USA,HKG)
+ **tag**: str. Current status of tracking. Values include `Pending`, `InfoReceived`, `InTransit`, `OutForDelivery`, `AttemptFail`, `Delivered`, `AvailableForPickup`, `Exception`, `Expired` (See tag definition)
+ **created_at_min**: str. Start date and time of trackings created. AfterShip only stores data of 120 days.(Defaults: 30 days ago, Example: 2013-03-15T16:41:56+08:00)
+ **created_at_max**: str. End date and time of trackings created.(Defaults: now, Example: 2013-04-15T16:41:56+08:00)
+ **updated_at_min**: str. Start date and time of trackings updated. (Example: 2013-04-15T16:41:56+08:00)
+ **updated_at_max**: str. End date and time of trackings updated. (Example: 2013-04-15T16:41:56+08:00)
+ **fields**: str. List of fields to include in the response. Use comma for multiple values. Available options: `title`, `order_id`, `tag`, `checkpoints`. Example: `title,order_id`
+ **return_to_sender**: str. Select return to sender, the value should be `true` or `false`, with optional comma separated.
+ **courier_destination_country_iso3**: str. Destination country/region of trackings returned by courier. Use ISO Alpha-3 (three letters). Use comma for multiple values. (Example: USA,HKG)
+ **shipment_tags**: str. Tags you added to your shipments to help categorize and filter them easily. Use a comma to separate multiple values (Example: a,b)
+ **order_id**: str. A globally-unique identifier for the order. Use comma for multiple values.(Example: 6845a095a27a4caeb27487806f058add,4845a095a27a4caeb27487806f058abc)
+ """
+ url = "/tracking/2024-04/trackings"
+ params_keys = {
+ "page",
+ "limit",
+ "keyword",
+ "tracking_numbers",
+ "slug",
+ "transit_time",
+ "origin",
+ "destination",
+ "tag",
+ "created_at_min",
+ "created_at_max",
+ "updated_at_min",
+ "updated_at_max",
+ "fields",
+ "return_to_sender",
+ "courier_destination_country_iso3",
+ "shipment_tags",
+ "order_id",
+ }
+ params = {key: kwargs.pop(key) for key in params_keys if key in kwargs}
+
+ result = self._request("GET", url=url, params=params, **kwargs)
+ return GetTrackingsResponse().from_dict(
+ {
+ "pagination": {
+ "total": result.get("count"),
+ "page": result.get("page"),
+ "limit": result.get("limit"),
+ },
+ "trackings": result.get("trackings"),
+ }
+ )
+
+ @validate_params
+ def mark_tracking_completed_by_id(
+ self,
+ tracking_id: Annotated[str, Field(min_length=1)],
+ mark_tracking_completed_by_id_request: Union[MarkTrackingCompletedByIdRequest, dict],
+ **kwargs,
+ ) -> Tracking:
+ """
+ Mark a tracking as completed. The tracking won't auto update until retrack it.
+ :param tracking_id: str. tracking id.
+ :param mark_tracking_completed_by_id_request:
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ """
+ url = f"/tracking/2024-04/trackings/{tracking_id}/mark-as-completed"
+
+ body = mark_tracking_completed_by_id_request
+ if not isinstance(body, dict):
+ body = mark_tracking_completed_by_id_request.model_dump(exclude_none=True)
+ body = json.dumps(body)
+
+ result = self._request("POST", url=url, body=body, **kwargs)
+ return Tracking().from_dict(result.get("tracking"))
+
+ @validate_params
+ def mark_tracking_completed_by_slug_tracking_number(
+ self,
+ slug: Annotated[str, Field(min_length=1)],
+ tracking_number: Annotated[str, Field(min_length=1)],
+ mark_tracking_completed_by_slug_tracking_number_request: Union[
+ MarkTrackingCompletedBySlugTrackingNumberRequest, dict
+ ],
+ **kwargs,
+ ) -> Tracking:
+ """
+ Mark a tracking as completed. The tracking won't auto update until retrack it.
+ :param slug: str. Tracking slug.
+ :param tracking_number: str. Tracking number.
+ :param mark_tracking_completed_by_slug_tracking_number_request:
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ query params:
+ **tracking_account_number**: str. Additional field required by some carriers to retrieve the tracking info. The shipper’s carrier account number. Refer to our article on for more details.
+ **tracking_origin_country**: str. Additional field required by some carriers to retrieve the tracking info. The origin country/region of the shipment. Refer to our article on for more details.
+ **tracking_destination_country**: str. Additional field required by some carriers to retrieve the tracking info. The destination country/region of the shipment. Refer to our article on for more details.
+ **tracking_key**: str. Additional field required by some carriers to retrieve the tracking info. A type of tracking credential required by some carriers. Refer to our article on for more details.
+ **tracking_postal_code**: str. Additional field required by some carriers to retrieve the tracking info. The postal code of the recipient’s address. Refer to our article on for more details.
+ **tracking_ship_date**: str. Additional field required by some carriers to retrieve the tracking info. The date the shipment was sent, using the format YYYYMMDD. Refer to our article on for more details.
+ **tracking_state**: str. Additional field required by some carriers to retrieve the tracking info. The state/province of the recipient’s address. Refer to our article on for more details.
+ """
+ url = f"/tracking/2024-04/trackings/{slug}/{tracking_number}/mark-as-completed"
+ params_keys = {
+ "tracking_account_number",
+ "tracking_origin_country",
+ "tracking_destination_country",
+ "tracking_key",
+ "tracking_postal_code",
+ "tracking_ship_date",
+ "tracking_state",
+ }
+ params = {key: kwargs.pop(key) for key in params_keys if key in kwargs}
+
+ body = mark_tracking_completed_by_slug_tracking_number_request
+ if not isinstance(body, dict):
+ body = mark_tracking_completed_by_slug_tracking_number_request.model_dump(
+ exclude_none=True
+ )
+ body = json.dumps(body)
+
+ result = self._request("POST", url=url, params=params, body=body, **kwargs)
+ return Tracking().from_dict(result.get("tracking"))
+
+ @validate_params
+ def retrack_tracking_by_id(
+ self, tracking_id: Annotated[str, Field(min_length=1)], **kwargs
+ ) -> PartialUpdateTracking:
+ """
+ Retrack an expired tracking. Max 3 times per tracking.
+ :param tracking_id: str. tracking id.
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ """
+ url = f"/tracking/2024-04/trackings/{tracking_id}/retrack"
+
+ result = self._request("POST", url=url, **kwargs)
+ return PartialUpdateTracking().from_dict(result.get("tracking"))
+
+ @validate_params
+ def retrack_tracking_by_slug_tracking_number(
+ self,
+ slug: Annotated[str, Field(min_length=1)],
+ tracking_number: Annotated[str, Field(min_length=1)],
+ **kwargs,
+ ) -> PartialUpdateTracking:
+ """
+ Retrack an expired tracking. Max 3 times per tracking.
+ :param slug: str. Tracking slug.
+ :param tracking_number: str. Tracking number.
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ query params:
+ **tracking_account_number**: str. Additional field required by some carriers to retrieve the tracking info. The shipper’s carrier account number. Refer to our article on for more details.
+ **tracking_origin_country**: str. Additional field required by some carriers to retrieve the tracking info. The origin country/region of the shipment. Refer to our article on for more details.
+ **tracking_destination_country**: str. Additional field required by some carriers to retrieve the tracking info. The destination country/region of the shipment. Refer to our article on for more details.
+ **tracking_key**: str. Additional field required by some carriers to retrieve the tracking info. A type of tracking credential required by some carriers. Refer to our article on for more details.
+ **tracking_postal_code**: str. Additional field required by some carriers to retrieve the tracking info. The postal code of the recipient’s address. Refer to our article on for more details.
+ **tracking_ship_date**: str. Additional field required by some carriers to retrieve the tracking info. The date the shipment was sent, using the format YYYYMMDD. Refer to our article on for more details.
+ **tracking_state**: str. Additional field required by some carriers to retrieve the tracking info. The state/province of the recipient’s address. Refer to our article on for more details.
+ """
+ url = f"/tracking/2024-04/trackings/{slug}/{tracking_number}/retrack"
+ params_keys = {
+ "tracking_account_number",
+ "tracking_origin_country",
+ "tracking_destination_country",
+ "tracking_key",
+ "tracking_postal_code",
+ "tracking_ship_date",
+ "tracking_state",
+ }
+ params = {key: kwargs.pop(key) for key in params_keys if key in kwargs}
+
+ result = self._request("POST", url=url, params=params, **kwargs)
+ return PartialUpdateTracking().from_dict(result.get("tracking"))
+
+ @validate_params
+ def update_tracking_by_id(
+ self,
+ tracking_id: Annotated[str, Field(min_length=1)],
+ update_tracking_by_id_request: Union[TrackingUpdateTrackingByIdRequest, dict],
+ **kwargs,
+ ) -> Tracking:
+ """
+ Update a tracking.
+ :param tracking_id: str. tracking ID.
+ :param update_tracking_by_id_request:
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ """
+ url = f"/tracking/2024-04/trackings/{tracking_id}"
+
+ body = update_tracking_by_id_request
+ if not isinstance(body, dict):
+ body = update_tracking_by_id_request.model_dump(exclude_none=True)
+ body = json.dumps({"tracking": body})
+
+ result = self._request("PUT", url=url, body=body, **kwargs)
+ return Tracking().from_dict(result.get("tracking"))
+
+ @validate_params
+ def update_tracking_by_slug_tracking_number(
+ self,
+ slug: Annotated[str, Field(min_length=1)],
+ tracking_number: Annotated[str, Field(min_length=1)],
+ update_tracking_by_slug_tracking_number_request: Union[
+ TrackingUpdateTrackingBySlugTrackingNumberRequest, dict
+ ],
+ **kwargs,
+ ) -> Tracking:
+ """
+ Update a tracking.
+ :param slug: str. Tracking slug.
+ :param tracking_number: str. Tracking number.
+ :param update_tracking_by_slug_tracking_number_request:
+ :param kwargs:
+ request options:
+ **headers** (dict): support custom headers.
+ **verify** bool|str|SSLContext: SSL certificates (a.k.a CA bundle) used to
+ verify the identity of requested hosts. Either `True` (default CA bundle),
+ a path to an SSL certificate file, an `ssl.SSLContext`, or `False`
+ (which will disable verification).
+ query params:
+ **tracking_account_number**: str. Additional field required by some carriers to retrieve the tracking info. The shipper’s carrier account number. Refer to our article on for more details.
+ **tracking_origin_country**: str. Additional field required by some carriers to retrieve the tracking info. The origin country/region of the shipment. Refer to our article on for more details.
+ **tracking_destination_country**: str. Additional field required by some carriers to retrieve the tracking info. The destination country/region of the shipment. Refer to our article on for more details.
+ **tracking_key**: str. Additional field required by some carriers to retrieve the tracking info. A type of tracking credential required by some carriers. Refer to our article on for more details.
+ **tracking_postal_code**: str. Additional field required by some carriers to retrieve the tracking info. The postal code of the recipient’s address. Refer to our article on for more details.
+ **tracking_ship_date**: str. Additional field required by some carriers to retrieve the tracking info. The date the shipment was sent, using the format YYYYMMDD. Refer to our article on for more details.
+ **tracking_state**: str. Additional field required by some carriers to retrieve the tracking info. The state/province of the recipient’s address. Refer to our article on for more details.
+ """
+ url = f"/tracking/2024-04/trackings/{slug}/{tracking_number}"
+ params_keys = {
+ "tracking_account_number",
+ "tracking_origin_country",
+ "tracking_destination_country",
+ "tracking_key",
+ "tracking_postal_code",
+ "tracking_ship_date",
+ "tracking_state",
+ }
+ params = {key: kwargs.pop(key) for key in params_keys if key in kwargs}
+
+ body = update_tracking_by_slug_tracking_number_request
+ if not isinstance(body, dict):
+ body = update_tracking_by_slug_tracking_number_request.model_dump(exclude_none=True)
+ body = json.dumps({"tracking": body})
+
+ result = self._request("PUT", url=url, params=params, body=body, **kwargs)
+ return Tracking().from_dict(result.get("tracking"))
diff --git a/tracking/auth.py b/tracking/auth.py
new file mode 100644
index 0000000..3273c3c
--- /dev/null
+++ b/tracking/auth.py
@@ -0,0 +1,131 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+import base64
+import hashlib
+import hmac
+from typing import Dict
+from datetime import datetime
+from urllib3.util import url
+
+from Crypto.PublicKey import RSA
+from Crypto.Signature import PKCS1_PSS
+from Crypto.Hash import SHA256
+
+ApiKey = "API_KEY"
+Aes = "AES"
+Rsa = "RSA"
+
+
+class Authenticator:
+ def __init__(self, api_key: str, api_secret: str, auth_type: str):
+ self._api_key: str = api_key
+ self._api_secret: str = api_secret
+ self._kind: str = auth_type
+
+ def sign(self, method: str, uri: str, headers: dict, body: str) -> Dict:
+ """
+ The SignString is generated by Method, Uri, Headers, Body from a HTTP(s) request.
+
+ :param method: str - request method.
+ :param uri: str - request URI.
+ :param headers: dict - request headers.
+ :param body: str - request body.
+ """
+ headers["as-api-key"] = self._api_key
+
+ if self._kind == ApiKey:
+ return headers
+
+ if self._kind != ApiKey:
+ headers["date"] = datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S GMT")
+ concat_header = self.canonical_header(headers)
+ concat_rs = self.canonical_resource(uri)
+ request_date = headers.get("date")
+ sign_str = self.sign_str(method, body, request_date, concat_header, concat_rs)
+
+ if self._kind == Aes:
+ headers["as-signature-hmac-sha256"] = self.hmac_signature(
+ sign_str, self._api_secret
+ )
+
+ if self._kind == Rsa:
+ headers["as-signature-rsa-sha256"] = self.rsa_encrypt(sign_str, self._api_secret)
+
+ return headers
+
+ @classmethod
+ def sign_str(
+ cls, method: str, body: str, date: str, concat_header: str, concat_resource: str
+ ) -> str:
+ content_md5 = ""
+ content_type = ""
+
+ if body is not None and body != "":
+ content_type = "application/json"
+ content_md5 = cls.md5_encode(body)
+
+ return "\n".join([method, content_md5, content_type, date, concat_header, concat_resource])
+
+ @classmethod
+ def canonical_header(cls, headers: Dict) -> str:
+ """
+ :param headers: dict.
+
+ To generate the canonicalized_headers:
+
+ 1. Extract all request headers with the as- prefix key. Kindly note that the headers with the as- prefix
+ are not limited to as-api-key, but also include other as- prefixed key such as as-store-id.
+ 2. Convert all the request header key to lowercase (except the header values case),
+ and sort the headers in ASCII code order.
+ 3. Remove leading spaces and trailing spaces from the header key and value.
+ 4. Concatenate each of the header key and value with :, to form a header pair
+ header_pair = header_key + ":" + header_value
+ 5. Concatenate all header pairs with the new line character (ASCII code 10).
+ """
+ if headers is None or len(headers) == 0:
+ return ""
+
+ result = {k.lower(): v.lstrip() for k, v in headers.items() if k.lower().startswith("as-")}
+ result = dict(sorted(result.items()))
+
+ return "\n".join([f"{k}:{v}" for k, v in result.items()])
+
+ @classmethod
+ def canonical_resource(cls, raw_url: str) -> str:
+ """
+ :param raw_url: str - raw request url.
+ Example : https://api.aftership.com/tracking/2024-04/trackings?key2=value2&key1=value1
+
+ :return canonical_url: str - canonical request url.
+ Example :/tracking/2024-04/trackings?key1=value1&key2=value2
+ """
+ u = url.parse_url(raw_url)
+ resource = u.path
+
+ if u.query is not None and u.query != "":
+ resource += "?" + u.query
+
+ return resource
+
+ @staticmethod
+ def md5_encode(source: str) -> str:
+ return hashlib.md5(source.encode("utf-8")).hexdigest().upper()
+
+ @staticmethod
+ def rsa_encrypt(sign_str: str, api_secret: str) -> str:
+ private_key = RSA.importKey(api_secret.encode("utf-8"))
+ cipher = PKCS1_PSS.new(private_key)
+ h = SHA256.new()
+ h.update(sign_str.encode("utf-8"))
+ signature = cipher.sign(h)
+ return base64.b64encode(signature).decode("utf-8")
+
+ @staticmethod
+ def hmac_signature(sign_str: str, api_secret: str) -> str:
+ signature = hmac.new(
+ bytes(api_secret, "utf-8"), msg=bytes(sign_str, "utf-8"), digestmod=hashlib.sha256
+ ).digest()
+ return base64.b64encode(signature).decode("utf-8")
diff --git a/tracking/client.py b/tracking/client.py
new file mode 100644
index 0000000..c7cebc1
--- /dev/null
+++ b/tracking/client.py
@@ -0,0 +1,29 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+__all__ = ["Client"]
+
+from typing import Optional
+
+from .configuration import Configuration
+from .api import (
+ TrackingApi,
+ CourierApi,
+ LastCheckpointApi,
+ NotificationApi,
+ EstimatedDeliveryDateApi,
+)
+
+
+class Client:
+ def __init__(self, configuration: Optional[Configuration] = None) -> None:
+ if configuration is None:
+ configuration = Configuration()
+
+ self.tracking = TrackingApi(configuration)
+ self.courier = CourierApi(configuration)
+ self.last_checkpoint = LastCheckpointApi(configuration)
+ self.notification = NotificationApi(configuration)
+ self.estimated_delivery_date = EstimatedDeliveryDateApi(configuration)
diff --git a/tracking/configuration.py b/tracking/configuration.py
new file mode 100644
index 0000000..df67dd5
--- /dev/null
+++ b/tracking/configuration.py
@@ -0,0 +1,112 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+import os
+
+from typing import Optional
+
+from tracking.auth import ApiKey, Rsa, Aes
+from tracking.exceptions import InvalidApiKeyError, InvalidOptionError, ErrorCodeEnum
+
+_sdkPrefix = "AFTERSHIP_TRACKING_SDK_"
+
+
+def _get_val_from_env_if_need(value, key_suffix, default=None):
+ if value is None:
+ key = f"{_sdkPrefix}{key_suffix}"
+ return os.environ.get(key, default)
+ return value
+
+
+class Configuration:
+ """This class contains various settings of the API client.
+
+ :param domain: str - Base url.
+ :param authentication_type: str - decide your authentication type.
+ :param api_key: str - AS API key(s).
+ :param api_secret: str - AS api secret.
+ :param max_retry: int - retry limit.
+ :param user_agent: user-agent string for AS.
+ :param timeout: int - milliseconds timeout.
+ :param proxy: str - proxy url string.
+ """
+
+ def __init__(
+ self,
+ domain: Optional[str] = None,
+ authentication_type: Optional[str] = None,
+ max_retry: Optional[int] = None,
+ user_agent: Optional[str] = None,
+ api_key: Optional[str] = None,
+ api_secret: Optional[str] = None,
+ proxy: Optional[str] = None,
+ timeout: Optional[int] = None,
+ ) -> None:
+ self.domain = str(
+ _get_val_from_env_if_need(
+ domain, key_suffix="DOMAIN", default="https://api.aftership.com"
+ )
+ )
+
+ self.authentication_type = str(
+ _get_val_from_env_if_need(
+ authentication_type, key_suffix="AUTHENTICATION_TYPE", default=ApiKey
+ )
+ )
+
+ self.max_retry = int(
+ _get_val_from_env_if_need(max_retry, key_suffix="MAX_RETRY", default=2)
+ )
+
+ self.user_agent = (
+ str(_get_val_from_env_if_need(user_agent, key_suffix="USER_AGENT", default="")) or None
+ )
+
+ self.api_key = (
+ str(_get_val_from_env_if_need(api_key, key_suffix="API_KEY", default="")) or None
+ )
+
+ self.api_secret = (
+ str(_get_val_from_env_if_need(api_secret, key_suffix="API_SECRET", default="")) or None
+ )
+
+ self.proxy = str(_get_val_from_env_if_need(proxy, key_suffix="PROXY", default="")) or None
+
+ self.timeout = int(_get_val_from_env_if_need(timeout, key_suffix="TIMEOUT", default=10000))
+
+ self._validate()
+
+ def _validate(self):
+ if self.domain is None or len(self.domain) == 0:
+ raise InvalidOptionError(
+ code=ErrorCodeEnum.INVALID_OPTION, message=f"Invalid option: domain={self.domain}"
+ )
+ if self.authentication_type not in {ApiKey, Rsa, Aes}:
+ raise InvalidOptionError(
+ code=ErrorCodeEnum.INVALID_OPTION,
+ message=f"Invalid option: authentication_type={self.authentication_type}",
+ )
+
+ if self.api_key is None or len(self.api_key) == 0:
+ raise InvalidApiKeyError(code=ErrorCodeEnum.INVALID_API_KEY, message="Invalid API key")
+
+ if self.authentication_type != ApiKey and (
+ self.api_secret is None or len(self.api_secret) == 0
+ ):
+ raise InvalidOptionError(
+ code=ErrorCodeEnum.INVALID_OPTION,
+ message=f"Invalid option: api_secret={self.api_secret}",
+ )
+
+ if self.max_retry > 10 or self.max_retry < 0:
+ raise InvalidOptionError(
+ code=ErrorCodeEnum.INVALID_OPTION,
+ message=f"Invalid option: max_retry={self.max_retry}",
+ )
+
+ if self.timeout <= 0:
+ raise InvalidOptionError(
+ code=ErrorCodeEnum.INVALID_OPTION, message=f"Invalid option: timeout={self.timeout}"
+ )
diff --git a/tracking/exceptions.py b/tracking/exceptions.py
new file mode 100644
index 0000000..b752b65
--- /dev/null
+++ b/tracking/exceptions.py
@@ -0,0 +1,121 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from typing import Optional
+from enum import Enum, unique
+
+
+class ApiException(Exception):
+ """
+ The base exception class for all ApiException
+
+ :param code: Error code enum. Would be described in the following sections.
+ :param status_code: Same as the http status code. Must have a value.
+ :param message: Error returned by the API: same as meta.message, Error not returned by the API: error object default message
+ :param meta_code: Could be null for the error that is not returned by the API,
+ for example, timeout error, network error.
+ :param response_body: response.body, if any.
+ """
+
+ def __init__(
+ self,
+ code: str,
+ message: str,
+ status_code: Optional[int] = None,
+ meta_code: Optional[int] = None,
+ response_body: Optional[str] = None,
+ ) -> None:
+ super(ApiException, self).__init__()
+ self.meta_code = meta_code
+ self.message = message
+ self.code = code
+ self.status_code = status_code
+ self.response_body = response_body
+
+ def __str__(self):
+ return "{}: {}".format(self.__class__.__name__, self.message)
+
+
+class BadRequestError(ApiException):
+ pass
+
+
+class UnauthorizedError(ApiException):
+ pass
+
+
+class ForbiddenError(ApiException):
+ pass
+
+
+class NotFoundError(ApiException):
+ pass
+
+
+class TooManyRequestsError(ApiException):
+ pass
+
+
+class InternalError(ApiException):
+ pass
+
+
+class TimedOutError(ApiException):
+ pass
+
+
+class UnknownError(ApiException):
+ pass
+
+
+class InvalidApiKeyError(BadRequestError):
+ pass
+
+
+class InvalidOptionError(BadRequestError):
+ pass
+
+
+class RateLimitExceedError(TooManyRequestsError):
+ pass
+
+
+@unique
+class ErrorCodeEnum(Enum):
+ """
+ All available additional fields
+
+ allowed enum values
+ """
+
+ INVALID_API_KEY = "INVALID_API_KEY"
+ INVALID_OPTION = "INVALID_OPTION"
+ BAD_REQUEST = "BAD_REQUEST"
+ RATE_LIMIT_EXCEED = "RATE_LIMIT_EXCEED"
+ TIMED_OUT = "TIMED_OUT"
+ UNKNOW_ERROR = "UNKNOW_ERROR"
+
+ INVALID_REQUEST = "INVALID_REQUEST"
+ INVALID_JSON = "INVALID_JSON"
+ TRACKING_ALREADY_EXIST = "TRACKING_ALREADY_EXIST"
+ TRACKING_DOES_NOT_EXIST = "TRACKING_DOES_NOT_EXIST"
+ TRACKING_NUMBER_INVALID = "TRACKING_NUMBER_INVALID"
+ TRACKING_REQUIRED = "TRACKING_REQUIRED"
+ TRACKING_NUMBER_REQUIRED = "TRACKING_NUMBER_REQUIRED"
+ VALUE_INVALID = "VALUE_INVALID"
+ VALUE_REQUIRED = "VALUE_REQUIRED"
+ SLUG_INVALID = "SLUG_INVALID"
+ MISSING_OR_INVALID_REQUIRED_FIELD = "MISSING_OR_INVALID_REQUIRED_FIELD"
+ BAD_COURIER = "BAD_COURIER"
+ INACTIVE_RETRACK_NOT_ALLOWED = "INACTIVE_RETRACK_NOT_ALLOWED"
+ NOTIFICATION_REUQIRED = "NOTIFICATION_REUQIRED"
+ ID_INVALID = "ID_INVALID"
+ RETRACK_ONCE_ALLOWED = "RETRACK_ONCE_ALLOWED"
+ TRACKING_NUMBER_FORMAT_INVALID = "TRACKING_NUMBER_FORMAT_INVALID"
+ API_KEY_INVALID = "API_KEY_INVALID"
+ REQUEST_NOT_ALLOWED = "REQUEST_NOT_ALLOWED"
+ NOT_FOUND = "NOT_FOUND"
+ TOO_MANY_REQUEST = "TOO_MANY_REQUEST"
+ INTERNAL_ERROR = "INTERNAL_ERROR"
diff --git a/tracking/models/__init__.py b/tracking/models/__init__.py
new file mode 100644
index 0000000..032713d
--- /dev/null
+++ b/tracking/models/__init__.py
@@ -0,0 +1,152 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+__all__ = [
+ "AdditionalFieldsV1",
+ "AftershipEstimatedDeliveryDateTracking",
+ "CarbonEmissionsTracking",
+ "Checkpoint",
+ "CoordinateCheckpoint",
+ "Courier",
+ "CourierResponseV1",
+ "CustomEstimatedDeliveryDateTracking",
+ "CustomFieldsTrackingUpdateTrackingBySlugTrackingNumberRequest",
+ "DataCourierResponseV1",
+ "DataNotificationResponseV1",
+ "DataTrackingDeleteResponseV1",
+ "DataTrackingResponseGetMultipleV1",
+ "DataTrackingResponseV1",
+ "DestinationAddressEstimatedDeliveryDateRequest",
+ "DestinationAddressEstimatedDeliveryDateResponse",
+ "DetectCourierResponse",
+ "EstimatedDeliveryDateRequest",
+ "EstimatedDeliveryDateResponse",
+ "EstimatedPickupEstimatedDeliveryDateRequest",
+ "EstimatedPickupEstimatedDeliveryDateResponse",
+ "EventsCheckpoint",
+ "FirstEstimatedDeliveryTracking",
+ "GetAllCouriersResponse",
+ "GetCheckpointBySlugTrackingNumberResponse",
+ "GetCheckpointByTrackingIdResponse",
+ "GetTrackingsResponse",
+ "GetUserCouriersResponse",
+ "LatestEstimatedDeliveryTracking",
+ "MarkTrackingCompletedByIdRequest",
+ "MarkTrackingCompletedBySlugTrackingNumberRequest",
+ "MetaV1",
+ "NextCouriersTracking",
+ "NextCouriersTrackingCreateTrackingRequest",
+ "Notification",
+ "NotificationRequestV1",
+ "NotificationResponseV1",
+ "OrderProcessingTimeEstimatedPickupEstimatedDeliveryDateRequest",
+ "OrderProcessingTimeEstimatedPickupEstimatedDeliveryDateResponse",
+ "OriginAddressEstimatedDeliveryDateRequest",
+ "OriginAddressEstimatedDeliveryDateResponse",
+ "Pagination",
+ "PartialDeleteTracking",
+ "PartialUpdateTracking",
+ "PredictBatchRequest",
+ "PredictBatchResponse",
+ "ReasonEventsCheckpoint",
+ "SlugGroupV1",
+ "TagV1",
+ "Tracking",
+ "TrackingCreateTrackingRequest",
+ "TrackingDeleteResponseV1",
+ "TrackingDetectCourierRequest",
+ "TrackingResponseGetMultipleV1",
+ "TrackingResponseV1",
+ "TrackingUpdateTrackingByIdRequest",
+ "TrackingUpdateTrackingBySlugTrackingNumberRequest",
+ "WeightEstimatedDeliveryDateRequest",
+ "WeightEstimatedDeliveryDateResponse",
+]
+
+from .additional_fields_v1 import AdditionalFieldsV1
+from .aftership_estimated_delivery_date_tracking import AftershipEstimatedDeliveryDateTracking
+from .carbon_emissions_tracking import CarbonEmissionsTracking
+from .checkpoint import Checkpoint
+from .coordinate_checkpoint import CoordinateCheckpoint
+from .courier import Courier
+from .courier_response_v1 import CourierResponseV1
+from .custom_estimated_delivery_date_tracking import CustomEstimatedDeliveryDateTracking
+from .custom_fields_tracking_update_tracking_by_slug_tracking_number_request import (
+ CustomFieldsTrackingUpdateTrackingBySlugTrackingNumberRequest,
+)
+from .data_courier_response_v1 import DataCourierResponseV1
+from .data_notification_response_v1 import DataNotificationResponseV1
+from .data_tracking_delete_response_v1 import DataTrackingDeleteResponseV1
+from .data_tracking_response_get_multiple_v1 import DataTrackingResponseGetMultipleV1
+from .data_tracking_response_v1 import DataTrackingResponseV1
+from .destination_address_estimated_delivery_date_request import (
+ DestinationAddressEstimatedDeliveryDateRequest,
+)
+from .destination_address_estimated_delivery_date_response import (
+ DestinationAddressEstimatedDeliveryDateResponse,
+)
+from .detect_courier_response import DetectCourierResponse
+from .estimated_delivery_date_request import EstimatedDeliveryDateRequest
+from .estimated_delivery_date_response import EstimatedDeliveryDateResponse
+from .estimated_pickup_estimated_delivery_date_request import (
+ EstimatedPickupEstimatedDeliveryDateRequest,
+)
+from .estimated_pickup_estimated_delivery_date_response import (
+ EstimatedPickupEstimatedDeliveryDateResponse,
+)
+from .events_checkpoint import EventsCheckpoint
+from .first_estimated_delivery_tracking import FirstEstimatedDeliveryTracking
+from .get_all_couriers_response import GetAllCouriersResponse
+from .get_checkpoint_by_slug_tracking_number_response import (
+ GetCheckpointBySlugTrackingNumberResponse,
+)
+from .get_checkpoint_by_tracking_id_response import GetCheckpointByTrackingIdResponse
+from .get_trackings_response import GetTrackingsResponse
+from .get_user_couriers_response import GetUserCouriersResponse
+from .latest_estimated_delivery_tracking import LatestEstimatedDeliveryTracking
+from .mark_tracking_completed_by_id_request import MarkTrackingCompletedByIdRequest
+from .mark_tracking_completed_by_slug_tracking_number_request import (
+ MarkTrackingCompletedBySlugTrackingNumberRequest,
+)
+from .meta_v1 import MetaV1
+from .next_couriers_tracking import NextCouriersTracking
+from .next_couriers_tracking_create_tracking_request import (
+ NextCouriersTrackingCreateTrackingRequest,
+)
+from .notification import Notification
+from .notification_request_v1 import NotificationRequestV1
+from .notification_response_v1 import NotificationResponseV1
+from .order_processing_time_estimated_pickup_estimated_delivery_date_request import (
+ OrderProcessingTimeEstimatedPickupEstimatedDeliveryDateRequest,
+)
+from .order_processing_time_estimated_pickup_estimated_delivery_date_response import (
+ OrderProcessingTimeEstimatedPickupEstimatedDeliveryDateResponse,
+)
+from .origin_address_estimated_delivery_date_request import (
+ OriginAddressEstimatedDeliveryDateRequest,
+)
+from .origin_address_estimated_delivery_date_response import (
+ OriginAddressEstimatedDeliveryDateResponse,
+)
+from .pagination import Pagination
+from .partial_delete_tracking import PartialDeleteTracking
+from .partial_update_tracking import PartialUpdateTracking
+from .predict_batch_request import PredictBatchRequest
+from .predict_batch_response import PredictBatchResponse
+from .reason_events_checkpoint import ReasonEventsCheckpoint
+from .slug_group_v1 import SlugGroupV1
+from .tag_v1 import TagV1
+from .tracking import Tracking
+from .tracking_create_tracking_request import TrackingCreateTrackingRequest
+from .tracking_delete_response_v1 import TrackingDeleteResponseV1
+from .tracking_detect_courier_request import TrackingDetectCourierRequest
+from .tracking_response_get_multiple_v1 import TrackingResponseGetMultipleV1
+from .tracking_response_v1 import TrackingResponseV1
+from .tracking_update_tracking_by_id_request import TrackingUpdateTrackingByIdRequest
+from .tracking_update_tracking_by_slug_tracking_number_request import (
+ TrackingUpdateTrackingBySlugTrackingNumberRequest,
+)
+from .weight_estimated_delivery_date_request import WeightEstimatedDeliveryDateRequest
+from .weight_estimated_delivery_date_response import WeightEstimatedDeliveryDateResponse
diff --git a/tracking/models/additional_fields_v1.py b/tracking/models/additional_fields_v1.py
new file mode 100644
index 0000000..2008133
--- /dev/null
+++ b/tracking/models/additional_fields_v1.py
@@ -0,0 +1,23 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from enum import Enum, unique
+
+
+@unique
+class AdditionalFieldsV1(Enum):
+ """
+ All available additional fields
+
+ allowed enum values
+ """
+
+ TRACKING_ACCOUNT_NUMBER = "tracking_account_number"
+ TRACKING_POSTAL_CODE = "tracking_postal_code"
+ TRACKING_SHIP_DATE = "tracking_ship_date"
+ TRACKING_KEY = "tracking_key"
+ TRACKING_ORIGIN_COUNTRY = "tracking_origin_country"
+ TRACKING_DESTINATION_COUNTRY = "tracking_destination_country"
+ TRACKING_STATE = "tracking_state"
diff --git a/tracking/models/aftership_estimated_delivery_date_tracking.py b/tracking/models/aftership_estimated_delivery_date_tracking.py
new file mode 100644
index 0000000..6e451e1
--- /dev/null
+++ b/tracking/models/aftership_estimated_delivery_date_tracking.py
@@ -0,0 +1,39 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class AftershipEstimatedDeliveryDateTracking(BaseModel):
+ """
+ AftershipEstimatedDeliveryDateTracking
+ """ # noqa: E501
+
+ estimated_delivery_date: Optional[str] = None
+ confidence_code: Optional[float] = None
+ estimated_delivery_date_min: Optional[str] = None
+ estimated_delivery_date_max: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/carbon_emissions_tracking.py b/tracking/models/carbon_emissions_tracking.py
new file mode 100644
index 0000000..fb98048
--- /dev/null
+++ b/tracking/models/carbon_emissions_tracking.py
@@ -0,0 +1,37 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class CarbonEmissionsTracking(BaseModel):
+ """
+ CarbonEmissionsTracking
+ """ # noqa: E501
+
+ unit: Optional[str] = None
+ value: Optional[float] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/checkpoint.py b/tracking/models/checkpoint.py
new file mode 100644
index 0000000..9d6f81a
--- /dev/null
+++ b/tracking/models/checkpoint.py
@@ -0,0 +1,55 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, List, Optional
+from typing_extensions import Self
+
+from tracking.models.coordinate_checkpoint import CoordinateCheckpoint
+from tracking.models.tag_v1 import TagV1
+from tracking.models.events_checkpoint import EventsCheckpoint
+
+
+class Checkpoint(BaseModel):
+ """
+ Object describes checkpoint information.
+ """ # noqa: E501
+
+ created_at: Optional[str] = None
+ slug: Optional[str] = None
+ checkpoint_time: Optional[str] = None
+ location: Optional[str] = None
+ city: Optional[str] = None
+ state: Optional[str] = None
+ zip: Optional[str] = None
+ coordinate: Optional[CoordinateCheckpoint] = None
+ country_iso3: Optional[str] = None
+ country_name: Optional[str] = None
+ message: Optional[str] = None
+ tag: Optional[TagV1] = None
+ subtag: Optional[str] = None
+ subtag_message: Optional[str] = None
+ raw_tag: Optional[str] = None
+ events: Optional[List[EventsCheckpoint]] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/coordinate_checkpoint.py b/tracking/models/coordinate_checkpoint.py
new file mode 100644
index 0000000..b3a8330
--- /dev/null
+++ b/tracking/models/coordinate_checkpoint.py
@@ -0,0 +1,37 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class CoordinateCheckpoint(BaseModel):
+ """
+ CoordinateCheckpoint
+ """ # noqa: E501
+
+ latitude: Optional[float] = None
+ longitude: Optional[float] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/courier.py b/tracking/models/courier.py
new file mode 100644
index 0000000..1113784
--- /dev/null
+++ b/tracking/models/courier.py
@@ -0,0 +1,45 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, List, Optional
+from typing_extensions import Self
+
+
+class Courier(BaseModel):
+ """
+ Courier object
+ """ # noqa: E501
+
+ slug: Optional[str] = None
+ name: Optional[str] = None
+ phone: Optional[str] = None
+ other_name: Optional[str] = None
+ web_url: Optional[str] = None
+ required_fields: Optional[List[str]] = None
+ optional_fields: Optional[List[str]] = None
+ default_language: Optional[str] = None
+ support_languages: Optional[List[str]] = None
+ service_from_country_iso3: Optional[List[str]] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/courier_response_v1.py b/tracking/models/courier_response_v1.py
new file mode 100644
index 0000000..c72a40d
--- /dev/null
+++ b/tracking/models/courier_response_v1.py
@@ -0,0 +1,40 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+from tracking.models.meta_v1 import MetaV1
+from tracking.models.data_courier_response_v1 import DataCourierResponseV1
+
+
+class CourierResponseV1(BaseModel):
+ """
+ Model of all couriers endpoint response
+ """ # noqa: E501
+
+ meta: Optional[MetaV1] = None
+ data: Optional[DataCourierResponseV1] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/custom_estimated_delivery_date_tracking.py b/tracking/models/custom_estimated_delivery_date_tracking.py
new file mode 100644
index 0000000..3b955e4
--- /dev/null
+++ b/tracking/models/custom_estimated_delivery_date_tracking.py
@@ -0,0 +1,39 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class CustomEstimatedDeliveryDateTracking(BaseModel):
+ """
+ CustomEstimatedDeliveryDateTracking
+ """ # noqa: E501
+
+ type: Optional[str] = None
+ datetime: Optional[str] = None
+ datetime_min: Optional[str] = None
+ datetime_max: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/custom_fields_tracking_update_tracking_by_slug_tracking_number_request.py b/tracking/models/custom_fields_tracking_update_tracking_by_slug_tracking_number_request.py
new file mode 100644
index 0000000..5b6ec9a
--- /dev/null
+++ b/tracking/models/custom_fields_tracking_update_tracking_by_slug_tracking_number_request.py
@@ -0,0 +1,34 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class CustomFieldsTrackingUpdateTrackingBySlugTrackingNumberRequest(BaseModel):
+ """
+ CustomFieldsTrackingUpdateTrackingBySlugTrackingNumberRequest
+ """ # noqa: E501
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/data_courier_response_v1.py b/tracking/models/data_courier_response_v1.py
new file mode 100644
index 0000000..5fd3dd4
--- /dev/null
+++ b/tracking/models/data_courier_response_v1.py
@@ -0,0 +1,39 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, List, Optional
+from typing_extensions import Self
+
+from tracking.models.courier import Courier
+
+
+class DataCourierResponseV1(BaseModel):
+ """
+ DataCourierResponseV1
+ """ # noqa: E501
+
+ total: Optional[int] = None
+ couriers: Optional[List[Courier]] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/data_notification_response_v1.py b/tracking/models/data_notification_response_v1.py
new file mode 100644
index 0000000..0744cbe
--- /dev/null
+++ b/tracking/models/data_notification_response_v1.py
@@ -0,0 +1,38 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+from tracking.models.notification import Notification
+
+
+class DataNotificationResponseV1(BaseModel):
+ """
+ DataNotificationResponseV1
+ """ # noqa: E501
+
+ notification: Optional[Notification] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/data_tracking_delete_response_v1.py b/tracking/models/data_tracking_delete_response_v1.py
new file mode 100644
index 0000000..79dc530
--- /dev/null
+++ b/tracking/models/data_tracking_delete_response_v1.py
@@ -0,0 +1,38 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+from tracking.models.partial_delete_tracking import PartialDeleteTracking
+
+
+class DataTrackingDeleteResponseV1(BaseModel):
+ """
+ DataTrackingDeleteResponseV1
+ """ # noqa: E501
+
+ tracking: Optional[PartialDeleteTracking] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/data_tracking_response_get_multiple_v1.py b/tracking/models/data_tracking_response_get_multiple_v1.py
new file mode 100644
index 0000000..6950dc2
--- /dev/null
+++ b/tracking/models/data_tracking_response_get_multiple_v1.py
@@ -0,0 +1,52 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, List, Optional
+from typing_extensions import Self
+
+from tracking.models.tag_v1 import TagV1
+from tracking.models.tracking import Tracking
+
+
+class DataTrackingResponseGetMultipleV1(BaseModel):
+ """
+ DataTrackingResponseGetMultipleV1
+ """ # noqa: E501
+
+ page: Optional[int] = None
+ limit: Optional[int] = None
+ count: Optional[int] = None
+ keyword: Optional[str] = None
+ slug: Optional[str] = None
+ origin: Optional[List[str]] = None
+ destination: Optional[List[str]] = None
+ tag: Optional[TagV1] = None
+ created_at_min: Optional[str] = None
+ created_at_max: Optional[str] = None
+ last_updated_at: Optional[str] = None
+ return_to_sender: Optional[List[bool]] = None
+ courier_destination_country_iso3: Optional[List[str]] = None
+ trackings: Optional[List[Tracking]] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/data_tracking_response_v1.py b/tracking/models/data_tracking_response_v1.py
new file mode 100644
index 0000000..45e3e77
--- /dev/null
+++ b/tracking/models/data_tracking_response_v1.py
@@ -0,0 +1,38 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+from tracking.models.tracking import Tracking
+
+
+class DataTrackingResponseV1(BaseModel):
+ """
+ DataTrackingResponseV1
+ """ # noqa: E501
+
+ tracking: Optional[Tracking] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/destination_address_estimated_delivery_date_request.py b/tracking/models/destination_address_estimated_delivery_date_request.py
new file mode 100644
index 0000000..89c8e09
--- /dev/null
+++ b/tracking/models/destination_address_estimated_delivery_date_request.py
@@ -0,0 +1,40 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class DestinationAddressEstimatedDeliveryDateRequest(BaseModel):
+ """
+ DestinationAddressEstimatedDeliveryDateRequest
+ """ # noqa: E501
+
+ country: Optional[str] = None
+ state: Optional[str] = None
+ city: Optional[str] = None
+ postal_code: Optional[str] = None
+ raw_location: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/destination_address_estimated_delivery_date_response.py b/tracking/models/destination_address_estimated_delivery_date_response.py
new file mode 100644
index 0000000..cb4ae16
--- /dev/null
+++ b/tracking/models/destination_address_estimated_delivery_date_response.py
@@ -0,0 +1,40 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class DestinationAddressEstimatedDeliveryDateResponse(BaseModel):
+ """
+ DestinationAddressEstimatedDeliveryDateResponse
+ """ # noqa: E501
+
+ country: Optional[str] = None
+ state: Optional[str] = None
+ city: Optional[str] = None
+ postal_code: Optional[str] = None
+ raw_location: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/detect_courier_response.py b/tracking/models/detect_courier_response.py
new file mode 100644
index 0000000..71005bc
--- /dev/null
+++ b/tracking/models/detect_courier_response.py
@@ -0,0 +1,39 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, List, Optional
+from typing_extensions import Self
+
+from tracking.models.courier import Courier
+
+
+class DetectCourierResponse(BaseModel):
+ """
+ DetectCourierResponse
+ """ # noqa: E501
+
+ total: Optional[int] = None
+ couriers: Optional[List[Courier]] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/estimated_delivery_date_request.py b/tracking/models/estimated_delivery_date_request.py
new file mode 100644
index 0000000..55fd84f
--- /dev/null
+++ b/tracking/models/estimated_delivery_date_request.py
@@ -0,0 +1,56 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+from tracking.models.origin_address_estimated_delivery_date_request import (
+ OriginAddressEstimatedDeliveryDateRequest,
+)
+from tracking.models.destination_address_estimated_delivery_date_request import (
+ DestinationAddressEstimatedDeliveryDateRequest,
+)
+from tracking.models.weight_estimated_delivery_date_request import (
+ WeightEstimatedDeliveryDateRequest,
+)
+from tracking.models.estimated_pickup_estimated_delivery_date_request import (
+ EstimatedPickupEstimatedDeliveryDateRequest,
+)
+
+
+class EstimatedDeliveryDateRequest(BaseModel):
+ """
+ EstimatedDeliveryDateRequest
+ """ # noqa: E501
+
+ slug: Optional[str] = None
+ service_type_name: Optional[str] = None
+ origin_address: Optional[OriginAddressEstimatedDeliveryDateRequest] = None
+ destination_address: Optional[DestinationAddressEstimatedDeliveryDateRequest] = None
+ weight: Optional[WeightEstimatedDeliveryDateRequest] = None
+ package_count: Optional[int] = None
+ pickup_time: Optional[str] = None
+ estimated_pickup: Optional[EstimatedPickupEstimatedDeliveryDateRequest] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/estimated_delivery_date_response.py b/tracking/models/estimated_delivery_date_response.py
new file mode 100644
index 0000000..4b087c1
--- /dev/null
+++ b/tracking/models/estimated_delivery_date_response.py
@@ -0,0 +1,60 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+from tracking.models.origin_address_estimated_delivery_date_response import (
+ OriginAddressEstimatedDeliveryDateResponse,
+)
+from tracking.models.destination_address_estimated_delivery_date_response import (
+ DestinationAddressEstimatedDeliveryDateResponse,
+)
+from tracking.models.weight_estimated_delivery_date_response import (
+ WeightEstimatedDeliveryDateResponse,
+)
+from tracking.models.estimated_pickup_estimated_delivery_date_response import (
+ EstimatedPickupEstimatedDeliveryDateResponse,
+)
+
+
+class EstimatedDeliveryDateResponse(BaseModel):
+ """
+ EstimatedDeliveryDateResponse
+ """ # noqa: E501
+
+ slug: Optional[str] = None
+ service_type_name: Optional[str] = None
+ origin_address: Optional[OriginAddressEstimatedDeliveryDateResponse] = None
+ destination_address: Optional[DestinationAddressEstimatedDeliveryDateResponse] = None
+ weight: Optional[WeightEstimatedDeliveryDateResponse] = None
+ package_count: Optional[int] = None
+ pickup_time: Optional[str] = None
+ estimated_pickup: Optional[EstimatedPickupEstimatedDeliveryDateResponse] = None
+ estimated_delivery_date: Optional[str] = None
+ confidence_code: Optional[float] = None
+ estimated_delivery_date_min: Optional[str] = None
+ estimated_delivery_date_max: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/estimated_pickup_estimated_delivery_date_request.py b/tracking/models/estimated_pickup_estimated_delivery_date_request.py
new file mode 100644
index 0000000..315c523
--- /dev/null
+++ b/tracking/models/estimated_pickup_estimated_delivery_date_request.py
@@ -0,0 +1,45 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, List, Optional
+from typing_extensions import Self
+
+from tracking.models.order_processing_time_estimated_pickup_estimated_delivery_date_request import (
+ OrderProcessingTimeEstimatedPickupEstimatedDeliveryDateRequest,
+)
+
+
+class EstimatedPickupEstimatedDeliveryDateRequest(BaseModel):
+ """
+ EstimatedPickupEstimatedDeliveryDateRequest
+ """ # noqa: E501
+
+ order_time: Optional[str] = None
+ order_cutoff_time: Optional[str] = None
+ business_days: Optional[List[int]] = None
+ order_processing_time: Optional[
+ OrderProcessingTimeEstimatedPickupEstimatedDeliveryDateRequest
+ ] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/estimated_pickup_estimated_delivery_date_response.py b/tracking/models/estimated_pickup_estimated_delivery_date_response.py
new file mode 100644
index 0000000..3882fba
--- /dev/null
+++ b/tracking/models/estimated_pickup_estimated_delivery_date_response.py
@@ -0,0 +1,46 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, List, Optional
+from typing_extensions import Self
+
+from tracking.models.order_processing_time_estimated_pickup_estimated_delivery_date_response import (
+ OrderProcessingTimeEstimatedPickupEstimatedDeliveryDateResponse,
+)
+
+
+class EstimatedPickupEstimatedDeliveryDateResponse(BaseModel):
+ """
+ EstimatedPickupEstimatedDeliveryDateResponse
+ """ # noqa: E501
+
+ order_time: Optional[str] = None
+ order_cutoff_time: Optional[str] = None
+ business_days: Optional[List[int]] = None
+ order_processing_time: Optional[
+ OrderProcessingTimeEstimatedPickupEstimatedDeliveryDateResponse
+ ] = None
+ pickup_time: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/events_checkpoint.py b/tracking/models/events_checkpoint.py
new file mode 100644
index 0000000..dab60e7
--- /dev/null
+++ b/tracking/models/events_checkpoint.py
@@ -0,0 +1,39 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+from tracking.models.reason_events_checkpoint import ReasonEventsCheckpoint
+
+
+class EventsCheckpoint(BaseModel):
+ """
+ EventsCheckpoint
+ """ # noqa: E501
+
+ code: Optional[str] = None
+ reason: Optional[ReasonEventsCheckpoint] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/first_estimated_delivery_tracking.py b/tracking/models/first_estimated_delivery_tracking.py
new file mode 100644
index 0000000..490234f
--- /dev/null
+++ b/tracking/models/first_estimated_delivery_tracking.py
@@ -0,0 +1,40 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class FirstEstimatedDeliveryTracking(BaseModel):
+ """
+ FirstEstimatedDeliveryTracking
+ """ # noqa: E501
+
+ type: Optional[str] = None
+ source: Optional[str] = None
+ datetime: Optional[str] = None
+ datetime_min: Optional[str] = None
+ datetime_max: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/get_all_couriers_response.py b/tracking/models/get_all_couriers_response.py
new file mode 100644
index 0000000..2277b26
--- /dev/null
+++ b/tracking/models/get_all_couriers_response.py
@@ -0,0 +1,39 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, List, Optional
+from typing_extensions import Self
+
+from tracking.models.courier import Courier
+
+
+class GetAllCouriersResponse(BaseModel):
+ """
+ GetAllCouriersResponse
+ """ # noqa: E501
+
+ total: Optional[int] = None
+ couriers: Optional[List[Courier]] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/get_checkpoint_by_slug_tracking_number_response.py b/tracking/models/get_checkpoint_by_slug_tracking_number_response.py
new file mode 100644
index 0000000..0b0e74a
--- /dev/null
+++ b/tracking/models/get_checkpoint_by_slug_tracking_number_response.py
@@ -0,0 +1,45 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+from tracking.models.tag_v1 import TagV1
+from tracking.models.checkpoint import Checkpoint
+
+
+class GetCheckpointBySlugTrackingNumberResponse(BaseModel):
+ """
+ GetCheckpointBySlugTrackingNumberResponse
+ """ # noqa: E501
+
+ id: Optional[str] = None
+ tracking_number: Optional[str] = None
+ slug: Optional[str] = None
+ tag: Optional[TagV1] = None
+ subtag: Optional[str] = None
+ subtag_message: Optional[str] = None
+ checkpoint: Optional[Checkpoint] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/get_checkpoint_by_tracking_id_response.py b/tracking/models/get_checkpoint_by_tracking_id_response.py
new file mode 100644
index 0000000..6ab656c
--- /dev/null
+++ b/tracking/models/get_checkpoint_by_tracking_id_response.py
@@ -0,0 +1,45 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+from tracking.models.tag_v1 import TagV1
+from tracking.models.checkpoint import Checkpoint
+
+
+class GetCheckpointByTrackingIdResponse(BaseModel):
+ """
+ GetCheckpointByTrackingIdResponse
+ """ # noqa: E501
+
+ id: Optional[str] = None
+ tracking_number: Optional[str] = None
+ slug: Optional[str] = None
+ tag: Optional[TagV1] = None
+ subtag: Optional[str] = None
+ subtag_message: Optional[str] = None
+ checkpoint: Optional[Checkpoint] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/get_trackings_response.py b/tracking/models/get_trackings_response.py
new file mode 100644
index 0000000..2d245c0
--- /dev/null
+++ b/tracking/models/get_trackings_response.py
@@ -0,0 +1,40 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, List, Optional
+from typing_extensions import Self
+
+from tracking.models.tracking import Tracking
+from tracking.models.pagination import Pagination
+
+
+class GetTrackingsResponse(BaseModel):
+ """
+ GetTrackingsResponse
+ """ # noqa: E501
+
+ pagination: Optional[Pagination] = None
+ trackings: Optional[List[Tracking]] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/get_user_couriers_response.py b/tracking/models/get_user_couriers_response.py
new file mode 100644
index 0000000..110efbe
--- /dev/null
+++ b/tracking/models/get_user_couriers_response.py
@@ -0,0 +1,39 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, List, Optional
+from typing_extensions import Self
+
+from tracking.models.courier import Courier
+
+
+class GetUserCouriersResponse(BaseModel):
+ """
+ GetUserCouriersResponse
+ """ # noqa: E501
+
+ total: Optional[int] = None
+ couriers: Optional[List[Courier]] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/latest_estimated_delivery_tracking.py b/tracking/models/latest_estimated_delivery_tracking.py
new file mode 100644
index 0000000..8eaff43
--- /dev/null
+++ b/tracking/models/latest_estimated_delivery_tracking.py
@@ -0,0 +1,40 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class LatestEstimatedDeliveryTracking(BaseModel):
+ """
+ LatestEstimatedDeliveryTracking
+ """ # noqa: E501
+
+ type: Optional[str] = None
+ source: Optional[str] = None
+ datetime: Optional[str] = None
+ datetime_min: Optional[str] = None
+ datetime_max: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/mark_tracking_completed_by_id_request.py b/tracking/models/mark_tracking_completed_by_id_request.py
new file mode 100644
index 0000000..c68879d
--- /dev/null
+++ b/tracking/models/mark_tracking_completed_by_id_request.py
@@ -0,0 +1,36 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class MarkTrackingCompletedByIdRequest(BaseModel):
+ """
+ MarkTrackingCompletedByIdRequest
+ """ # noqa: E501
+
+ reason: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/mark_tracking_completed_by_slug_tracking_number_request.py b/tracking/models/mark_tracking_completed_by_slug_tracking_number_request.py
new file mode 100644
index 0000000..297c7b0
--- /dev/null
+++ b/tracking/models/mark_tracking_completed_by_slug_tracking_number_request.py
@@ -0,0 +1,36 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class MarkTrackingCompletedBySlugTrackingNumberRequest(BaseModel):
+ """
+ MarkTrackingCompletedBySlugTrackingNumberRequest
+ """ # noqa: E501
+
+ reason: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/meta_v1.py b/tracking/models/meta_v1.py
new file mode 100644
index 0000000..f40b4b1
--- /dev/null
+++ b/tracking/models/meta_v1.py
@@ -0,0 +1,38 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class MetaV1(BaseModel):
+ """
+ Meta data
+ """ # noqa: E501
+
+ code: Optional[int] = None
+ message: Optional[str] = None
+ type: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/next_couriers_tracking.py b/tracking/models/next_couriers_tracking.py
new file mode 100644
index 0000000..da7c390
--- /dev/null
+++ b/tracking/models/next_couriers_tracking.py
@@ -0,0 +1,38 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class NextCouriersTracking(BaseModel):
+ """
+ NextCouriersTracking
+ """ # noqa: E501
+
+ slug: Optional[str] = None
+ tracking_number: Optional[str] = None
+ source: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/next_couriers_tracking_create_tracking_request.py b/tracking/models/next_couriers_tracking_create_tracking_request.py
new file mode 100644
index 0000000..52685af
--- /dev/null
+++ b/tracking/models/next_couriers_tracking_create_tracking_request.py
@@ -0,0 +1,37 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class NextCouriersTrackingCreateTrackingRequest(BaseModel):
+ """
+ NextCouriersTrackingCreateTrackingRequest
+ """ # noqa: E501
+
+ slug: Optional[str] = None
+ tracking_number: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/notification.py b/tracking/models/notification.py
new file mode 100644
index 0000000..14e7ec0
--- /dev/null
+++ b/tracking/models/notification.py
@@ -0,0 +1,37 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, List, Optional
+from typing_extensions import Self
+
+
+class Notification(BaseModel):
+ """
+ Object describes the notification information.
+ """ # noqa: E501
+
+ emails: Optional[List[str]] = None
+ smses: Optional[List[str]] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/notification_request_v1.py b/tracking/models/notification_request_v1.py
new file mode 100644
index 0000000..5c16440
--- /dev/null
+++ b/tracking/models/notification_request_v1.py
@@ -0,0 +1,37 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, List, Optional
+from typing_extensions import Self
+
+
+class NotificationRequestV1(BaseModel):
+ """
+ Notification request object
+ """ # noqa: E501
+
+ emails: Optional[List[str]] = None
+ smses: Optional[List[str]] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/notification_response_v1.py b/tracking/models/notification_response_v1.py
new file mode 100644
index 0000000..ca47048
--- /dev/null
+++ b/tracking/models/notification_response_v1.py
@@ -0,0 +1,40 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+from tracking.models.meta_v1 import MetaV1
+from tracking.models.data_notification_response_v1 import DataNotificationResponseV1
+
+
+class NotificationResponseV1(BaseModel):
+ """
+ NotificationResponseV1
+ """ # noqa: E501
+
+ meta: Optional[MetaV1] = None
+ data: Optional[DataNotificationResponseV1] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/order_processing_time_estimated_pickup_estimated_delivery_date_request.py b/tracking/models/order_processing_time_estimated_pickup_estimated_delivery_date_request.py
new file mode 100644
index 0000000..e0302bb
--- /dev/null
+++ b/tracking/models/order_processing_time_estimated_pickup_estimated_delivery_date_request.py
@@ -0,0 +1,37 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class OrderProcessingTimeEstimatedPickupEstimatedDeliveryDateRequest(BaseModel):
+ """
+ OrderProcessingTimeEstimatedPickupEstimatedDeliveryDateRequest
+ """ # noqa: E501
+
+ unit: Optional[str] = None
+ value: Optional[float] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/order_processing_time_estimated_pickup_estimated_delivery_date_response.py b/tracking/models/order_processing_time_estimated_pickup_estimated_delivery_date_response.py
new file mode 100644
index 0000000..8f81f62
--- /dev/null
+++ b/tracking/models/order_processing_time_estimated_pickup_estimated_delivery_date_response.py
@@ -0,0 +1,37 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class OrderProcessingTimeEstimatedPickupEstimatedDeliveryDateResponse(BaseModel):
+ """
+ OrderProcessingTimeEstimatedPickupEstimatedDeliveryDateResponse
+ """ # noqa: E501
+
+ unit: Optional[str] = None
+ value: Optional[float] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/origin_address_estimated_delivery_date_request.py b/tracking/models/origin_address_estimated_delivery_date_request.py
new file mode 100644
index 0000000..088cd8b
--- /dev/null
+++ b/tracking/models/origin_address_estimated_delivery_date_request.py
@@ -0,0 +1,40 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class OriginAddressEstimatedDeliveryDateRequest(BaseModel):
+ """
+ OriginAddressEstimatedDeliveryDateRequest
+ """ # noqa: E501
+
+ country: Optional[str] = None
+ state: Optional[str] = None
+ city: Optional[str] = None
+ postal_code: Optional[str] = None
+ raw_location: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/origin_address_estimated_delivery_date_response.py b/tracking/models/origin_address_estimated_delivery_date_response.py
new file mode 100644
index 0000000..a0ec24c
--- /dev/null
+++ b/tracking/models/origin_address_estimated_delivery_date_response.py
@@ -0,0 +1,40 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class OriginAddressEstimatedDeliveryDateResponse(BaseModel):
+ """
+ OriginAddressEstimatedDeliveryDateResponse
+ """ # noqa: E501
+
+ country: Optional[str] = None
+ state: Optional[str] = None
+ city: Optional[str] = None
+ postal_code: Optional[str] = None
+ raw_location: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/pagination.py b/tracking/models/pagination.py
new file mode 100644
index 0000000..ff1ead7
--- /dev/null
+++ b/tracking/models/pagination.py
@@ -0,0 +1,38 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class Pagination(BaseModel):
+ """
+ Pagination
+ """ # noqa: E501
+
+ total: Optional[int] = None
+ page: Optional[int] = None
+ limit: Optional[int] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/partial_delete_tracking.py b/tracking/models/partial_delete_tracking.py
new file mode 100644
index 0000000..8abd384
--- /dev/null
+++ b/tracking/models/partial_delete_tracking.py
@@ -0,0 +1,45 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class PartialDeleteTracking(BaseModel):
+ """
+ Partial tracking model
+ """ # noqa: E501
+
+ id: Optional[str] = None
+ tracking_number: Optional[str] = None
+ slug: Optional[str] = None
+ tracking_account_number: Optional[str] = None
+ tracking_key: Optional[str] = None
+ tracking_ship_date: Optional[str] = None
+ tracking_origin_country: Optional[str] = None
+ tracking_destination_country: Optional[str] = None
+ tracking_postal_code: Optional[str] = None
+ tracking_state: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/partial_update_tracking.py b/tracking/models/partial_update_tracking.py
new file mode 100644
index 0000000..9e3ee6c
--- /dev/null
+++ b/tracking/models/partial_update_tracking.py
@@ -0,0 +1,46 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class PartialUpdateTracking(BaseModel):
+ """
+ Partial tracking
+ """ # noqa: E501
+
+ id: Optional[str] = None
+ tracking_number: Optional[str] = None
+ slug: Optional[str] = None
+ tracking_account_number: Optional[str] = None
+ tracking_origin_country: Optional[str] = None
+ tracking_destination_country: Optional[str] = None
+ tracking_key: Optional[str] = None
+ tracking_ship_date: Optional[str] = None
+ tracking_postal_code: Optional[str] = None
+ tracking_state: Optional[str] = None
+ active: Optional[bool] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/predict_batch_request.py b/tracking/models/predict_batch_request.py
new file mode 100644
index 0000000..2812707
--- /dev/null
+++ b/tracking/models/predict_batch_request.py
@@ -0,0 +1,38 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, List, Optional
+from typing_extensions import Self
+
+from tracking.models.estimated_delivery_date_request import EstimatedDeliveryDateRequest
+
+
+class PredictBatchRequest(BaseModel):
+ """
+ PredictBatchRequest
+ """ # noqa: E501
+
+ estimated_delivery_dates: Optional[List[EstimatedDeliveryDateRequest]] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/predict_batch_response.py b/tracking/models/predict_batch_response.py
new file mode 100644
index 0000000..a41efa5
--- /dev/null
+++ b/tracking/models/predict_batch_response.py
@@ -0,0 +1,38 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, List, Optional
+from typing_extensions import Self
+
+from tracking.models.estimated_delivery_date_response import EstimatedDeliveryDateResponse
+
+
+class PredictBatchResponse(BaseModel):
+ """
+ PredictBatchResponse
+ """ # noqa: E501
+
+ estimated_delivery_dates: Optional[List[EstimatedDeliveryDateResponse]] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/reason_events_checkpoint.py b/tracking/models/reason_events_checkpoint.py
new file mode 100644
index 0000000..b5a799a
--- /dev/null
+++ b/tracking/models/reason_events_checkpoint.py
@@ -0,0 +1,36 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class ReasonEventsCheckpoint(BaseModel):
+ """
+ ReasonEventsCheckpoint
+ """ # noqa: E501
+
+ code: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/slug_group_v1.py b/tracking/models/slug_group_v1.py
new file mode 100644
index 0000000..593d5f6
--- /dev/null
+++ b/tracking/models/slug_group_v1.py
@@ -0,0 +1,28 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from enum import Enum, unique
+
+
+@unique
+class SlugGroupV1(Enum):
+ """
+ Slug group is a group of slugs which belong to same courier. For example, when you inpit "fedex-group" as slug_group, AfterShip will detect the tracking with "fedex-uk", "fedex-fims", and other slugs which belong to "fedex". It cannot be used with slug at the same time. (
+
+ allowed enum values
+ """
+
+ AMAZON_GROUP = "amazon-group"
+ FEDEX_GROUP = "fedex-group"
+ TOLL_GROUP = "toll-group"
+ TAQBIN_GROUP = "taqbin-group"
+ TNT_GROUP = "tnt-group"
+ CJ_GROUP = "cj-group"
+ HERMES_GROUP = "hermes-group"
+ DPD_GROUP = "dpd-group"
+ GLS_GROUP = "gls-group"
+ DHL_GROUP = "dhl-group"
+ FASTWAY_GROUP = "fastway-group"
+ ASENDIA_GROUP = "asendia-group"
diff --git a/tracking/models/tag_v1.py b/tracking/models/tag_v1.py
new file mode 100644
index 0000000..25e0fb6
--- /dev/null
+++ b/tracking/models/tag_v1.py
@@ -0,0 +1,25 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from enum import Enum, unique
+
+
+@unique
+class TagV1(Enum):
+ """
+ Current status of tracking. (
+
+ allowed enum values
+ """
+
+ PENDING = "Pending"
+ INFORECEIVED = "InfoReceived"
+ INTRANSIT = "InTransit"
+ OUTFORDELIVERY = "OutForDelivery"
+ ATTEMPTFAIL = "AttemptFail"
+ DELIVERED = "Delivered"
+ AVAILABLEFORPICKUP = "AvailableForPickup"
+ EXCEPTION = "Exception"
+ EXPIRED = "Expired"
diff --git a/tracking/models/tracking.py b/tracking/models/tracking.py
new file mode 100644
index 0000000..0968dbe
--- /dev/null
+++ b/tracking/models/tracking.py
@@ -0,0 +1,126 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, List, Optional
+from typing_extensions import Self
+
+from tracking.models.tag_v1 import TagV1
+from tracking.models.checkpoint import Checkpoint
+from tracking.models.aftership_estimated_delivery_date_tracking import (
+ AftershipEstimatedDeliveryDateTracking,
+)
+from tracking.models.custom_estimated_delivery_date_tracking import (
+ CustomEstimatedDeliveryDateTracking,
+)
+from tracking.models.first_estimated_delivery_tracking import FirstEstimatedDeliveryTracking
+from tracking.models.latest_estimated_delivery_tracking import LatestEstimatedDeliveryTracking
+from tracking.models.next_couriers_tracking import NextCouriersTracking
+from tracking.models.carbon_emissions_tracking import CarbonEmissionsTracking
+
+
+class Tracking(BaseModel):
+ """
+ Object describes the tracking information.
+ """ # noqa: E501
+
+ id: Optional[str] = None
+ created_at: Optional[str] = None
+ updated_at: Optional[str] = None
+ last_updated_at: Optional[str] = None
+ tracking_number: Optional[str] = None
+ slug: Optional[str] = None
+ active: Optional[bool] = None
+ custom_fields: Optional[Any] = None
+ customer_name: Optional[str] = None
+ transit_time: Optional[int] = None
+ origin_country_iso3: Optional[str] = None
+ origin_state: Optional[str] = None
+ origin_city: Optional[str] = None
+ origin_postal_code: Optional[str] = None
+ origin_raw_location: Optional[str] = None
+ destination_country_iso3: Optional[str] = None
+ destination_state: Optional[str] = None
+ destination_city: Optional[str] = None
+ destination_postal_code: Optional[str] = None
+ destination_raw_location: Optional[str] = None
+ courier_destination_country_iso3: Optional[str] = None
+ emails: Optional[List[str]] = None
+ expected_delivery: Optional[str] = None
+ note: Optional[str] = None
+ order_id: Optional[str] = None
+ order_id_path: Optional[str] = None
+ order_date: Optional[str] = None
+ shipment_package_count: Optional[float] = None
+ shipment_pickup_date: Optional[str] = None
+ shipment_delivery_date: Optional[str] = None
+ shipment_type: Optional[str] = None
+ shipment_weight: Optional[float] = None
+ shipment_weight_unit: Optional[str] = None
+ signed_by: Optional[str] = None
+ smses: Optional[List[str]] = None
+ source: Optional[str] = None
+ tag: Optional[TagV1] = None
+ subtag: Optional[str] = None
+ subtag_message: Optional[str] = None
+ title: Optional[str] = None
+ tracked_count: Optional[float] = None
+ last_mile_tracking_supported: Optional[bool] = None
+ language: Optional[str] = None
+ unique_token: Optional[str] = None
+ checkpoints: Optional[List[Checkpoint]] = None
+ subscribed_smses: Optional[List[str]] = None
+ subscribed_emails: Optional[List[str]] = None
+ return_to_sender: Optional[bool] = None
+ order_promised_delivery_date: Optional[str] = None
+ delivery_type: Optional[str] = None
+ pickup_location: Optional[str] = None
+ pickup_note: Optional[str] = None
+ courier_tracking_link: Optional[str] = None
+ first_attempted_at: Optional[str] = None
+ courier_redirect_link: Optional[str] = None
+ tracking_account_number: Optional[str] = None
+ tracking_key: Optional[str] = None
+ tracking_ship_date: Optional[str] = None
+ on_time_status: Optional[str] = None
+ on_time_difference: Optional[float] = None
+ order_tags: Optional[List[str]] = None
+ aftership_estimated_delivery_date: Optional[AftershipEstimatedDeliveryDateTracking] = None
+ custom_estimated_delivery_date: Optional[CustomEstimatedDeliveryDateTracking] = None
+ order_number: Optional[str] = None
+ first_estimated_delivery: Optional[FirstEstimatedDeliveryTracking] = None
+ latest_estimated_delivery: Optional[LatestEstimatedDeliveryTracking] = None
+ shipment_tags: Optional[List[str]] = None
+ courier_connection_id: Optional[str] = None
+ next_couriers: Optional[List[NextCouriersTracking]] = None
+ tracking_origin_country: Optional[str] = None
+ tracking_destination_country: Optional[str] = None
+ tracking_postal_code: Optional[str] = None
+ tracking_state: Optional[str] = None
+ carbon_emissions: Optional[CarbonEmissionsTracking] = None
+ location_id: Optional[str] = None
+ shipping_method: Optional[str] = None
+ failed_delivery_attempts: Optional[int] = None
+ signature_requirement: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/tracking_create_tracking_request.py b/tracking/models/tracking_create_tracking_request.py
new file mode 100644
index 0000000..001f77e
--- /dev/null
+++ b/tracking/models/tracking_create_tracking_request.py
@@ -0,0 +1,81 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, List, Optional
+from typing_extensions import Self
+
+from tracking.models.slug_group_v1 import SlugGroupV1
+from tracking.models.next_couriers_tracking_create_tracking_request import (
+ NextCouriersTrackingCreateTrackingRequest,
+)
+
+
+class TrackingCreateTrackingRequest(BaseModel):
+ """
+ TrackingCreateTrackingRequest
+ """ # noqa: E501
+
+ tracking_number: Optional[str] = None
+ slug: Optional[str] = None
+ title: Optional[str] = None
+ order_id: Optional[str] = None
+ order_id_path: Optional[str] = None
+ custom_fields: Optional[Any] = None
+ language: Optional[str] = None
+ order_promised_delivery_date: Optional[str] = None
+ delivery_type: Optional[str] = None
+ pickup_location: Optional[str] = None
+ pickup_note: Optional[str] = None
+ tracking_account_number: Optional[str] = None
+ tracking_key: Optional[str] = None
+ tracking_ship_date: Optional[str] = None
+ emails: Optional[List[str]] = None
+ smses: Optional[List[str]] = None
+ customer_name: Optional[str] = None
+ origin_country_iso3: Optional[str] = None
+ origin_state: Optional[str] = None
+ origin_city: Optional[str] = None
+ origin_postal_code: Optional[str] = None
+ origin_raw_location: Optional[str] = None
+ destination_country_iso3: Optional[str] = None
+ destination_state: Optional[str] = None
+ destination_city: Optional[str] = None
+ destination_postal_code: Optional[str] = None
+ destination_raw_location: Optional[str] = None
+ note: Optional[str] = None
+ slug_group: Optional[SlugGroupV1] = None
+ order_date: Optional[str] = None
+ order_number: Optional[str] = None
+ shipment_type: Optional[str] = None
+ shipment_tags: Optional[List[str]] = None
+ courier_connection_id: Optional[str] = None
+ next_couriers: Optional[List[NextCouriersTrackingCreateTrackingRequest]] = None
+ tracking_origin_country: Optional[str] = None
+ tracking_destination_country: Optional[str] = None
+ tracking_postal_code: Optional[str] = None
+ tracking_state: Optional[str] = None
+ location_id: Optional[str] = None
+ shipping_method: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/tracking_delete_response_v1.py b/tracking/models/tracking_delete_response_v1.py
new file mode 100644
index 0000000..f809af7
--- /dev/null
+++ b/tracking/models/tracking_delete_response_v1.py
@@ -0,0 +1,40 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+from tracking.models.meta_v1 import MetaV1
+from tracking.models.data_tracking_delete_response_v1 import DataTrackingDeleteResponseV1
+
+
+class TrackingDeleteResponseV1(BaseModel):
+ """
+ Tracking response for returning single tracking when deleting tracking
+ """ # noqa: E501
+
+ meta: Optional[MetaV1] = None
+ data: Optional[DataTrackingDeleteResponseV1] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/tracking_detect_courier_request.py b/tracking/models/tracking_detect_courier_request.py
new file mode 100644
index 0000000..aaddbaa
--- /dev/null
+++ b/tracking/models/tracking_detect_courier_request.py
@@ -0,0 +1,49 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, List, Optional
+from typing_extensions import Self
+
+from tracking.models.slug_group_v1 import SlugGroupV1
+
+
+class TrackingDetectCourierRequest(BaseModel):
+ """
+ TrackingDetectCourierRequest
+ """ # noqa: E501
+
+ tracking_number: Optional[str] = None
+ slug: Optional[List[str]] = None
+ tracking_postal_code: Optional[str] = None
+ tracking_ship_date: Optional[str] = None
+ tracking_account_number: Optional[str] = None
+ tracking_key: Optional[str] = None
+ tracking_origin_country: Optional[str] = None
+ tracking_destination_country: Optional[str] = None
+ tracking_state: Optional[str] = None
+ slug_group: Optional[SlugGroupV1] = None
+ origin_country_iso3: Optional[str] = None
+ destination_country_iso3: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/tracking_response_get_multiple_v1.py b/tracking/models/tracking_response_get_multiple_v1.py
new file mode 100644
index 0000000..642ca1f
--- /dev/null
+++ b/tracking/models/tracking_response_get_multiple_v1.py
@@ -0,0 +1,40 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+from tracking.models.meta_v1 import MetaV1
+from tracking.models.data_tracking_response_get_multiple_v1 import DataTrackingResponseGetMultipleV1
+
+
+class TrackingResponseGetMultipleV1(BaseModel):
+ """
+ Tracking response for getting tracking
+ """ # noqa: E501
+
+ meta: Optional[MetaV1] = None
+ data: Optional[DataTrackingResponseGetMultipleV1] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/tracking_response_v1.py b/tracking/models/tracking_response_v1.py
new file mode 100644
index 0000000..b345f4f
--- /dev/null
+++ b/tracking/models/tracking_response_v1.py
@@ -0,0 +1,40 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+from tracking.models.meta_v1 import MetaV1
+from tracking.models.data_tracking_response_v1 import DataTrackingResponseV1
+
+
+class TrackingResponseV1(BaseModel):
+ """
+ Tracking response for returning single tracking
+ """ # noqa: E501
+
+ meta: Optional[MetaV1] = None
+ data: Optional[DataTrackingResponseV1] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/tracking_update_tracking_by_id_request.py b/tracking/models/tracking_update_tracking_by_id_request.py
new file mode 100644
index 0000000..df28c09
--- /dev/null
+++ b/tracking/models/tracking_update_tracking_by_id_request.py
@@ -0,0 +1,71 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, List, Optional
+from typing_extensions import Self
+
+
+class TrackingUpdateTrackingByIdRequest(BaseModel):
+ """
+ TrackingUpdateTrackingByIdRequest
+ """ # noqa: E501
+
+ smses: Optional[List[str]] = None
+ emails: Optional[List[str]] = None
+ title: Optional[str] = None
+ customer_name: Optional[str] = None
+ order_id: Optional[str] = None
+ order_id_path: Optional[str] = None
+ custom_fields: Optional[Any] = None
+ note: Optional[str] = None
+ language: Optional[str] = None
+ order_promised_delivery_date: Optional[str] = None
+ delivery_type: Optional[str] = None
+ pickup_location: Optional[str] = None
+ pickup_note: Optional[str] = None
+ slug: Optional[str] = None
+ tracking_account_number: Optional[str] = None
+ tracking_key: Optional[str] = None
+ tracking_ship_date: Optional[str] = None
+ order_number: Optional[str] = None
+ order_date: Optional[str] = None
+ shipment_type: Optional[str] = None
+ origin_country_iso3: Optional[str] = None
+ origin_state: Optional[str] = None
+ origin_city: Optional[str] = None
+ origin_postal_code: Optional[str] = None
+ origin_raw_location: Optional[str] = None
+ destination_country_iso3: Optional[str] = None
+ destination_state: Optional[str] = None
+ destination_city: Optional[str] = None
+ destination_postal_code: Optional[str] = None
+ destination_raw_location: Optional[str] = None
+ tracking_origin_country: Optional[str] = None
+ tracking_destination_country: Optional[str] = None
+ tracking_postal_code: Optional[str] = None
+ tracking_state: Optional[str] = None
+ location_id: Optional[str] = None
+ shipping_method: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/tracking_update_tracking_by_slug_tracking_number_request.py b/tracking/models/tracking_update_tracking_by_slug_tracking_number_request.py
new file mode 100644
index 0000000..8772ad0
--- /dev/null
+++ b/tracking/models/tracking_update_tracking_by_slug_tracking_number_request.py
@@ -0,0 +1,64 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, List, Optional
+from typing_extensions import Self
+
+from tracking.models.custom_fields_tracking_update_tracking_by_slug_tracking_number_request import (
+ CustomFieldsTrackingUpdateTrackingBySlugTrackingNumberRequest,
+)
+
+
+class TrackingUpdateTrackingBySlugTrackingNumberRequest(BaseModel):
+ """
+ TrackingUpdateTrackingBySlugTrackingNumberRequest
+ """ # noqa: E501
+
+ smses: Optional[List[str]] = None
+ emails: Optional[List[str]] = None
+ title: Optional[str] = None
+ customer_name: Optional[str] = None
+ order_id: Optional[str] = None
+ order_id_path: Optional[str] = None
+ custom_fields: Optional[CustomFieldsTrackingUpdateTrackingBySlugTrackingNumberRequest] = None
+ note: Optional[str] = None
+ language: Optional[str] = None
+ order_promised_delivery_date: Optional[str] = None
+ delivery_type: Optional[str] = None
+ pickup_location: Optional[str] = None
+ pickup_note: Optional[str] = None
+ slug: Optional[str] = None
+ tracking_account_number: Optional[str] = None
+ tracking_origin_country: Optional[str] = None
+ tracking_destination_country: Optional[str] = None
+ tracking_key: Optional[str] = None
+ tracking_postal_code: Optional[str] = None
+ tracking_ship_date: Optional[str] = None
+ tracking_state: Optional[str] = None
+ order_number: Optional[str] = None
+ order_date: Optional[str] = None
+ location_id: Optional[str] = None
+ shipping_method: Optional[str] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/weight_estimated_delivery_date_request.py b/tracking/models/weight_estimated_delivery_date_request.py
new file mode 100644
index 0000000..f3937f8
--- /dev/null
+++ b/tracking/models/weight_estimated_delivery_date_request.py
@@ -0,0 +1,37 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class WeightEstimatedDeliveryDateRequest(BaseModel):
+ """
+ WeightEstimatedDeliveryDateRequest
+ """ # noqa: E501
+
+ unit: Optional[str] = None
+ value: Optional[float] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/models/weight_estimated_delivery_date_response.py b/tracking/models/weight_estimated_delivery_date_response.py
new file mode 100644
index 0000000..c84f770
--- /dev/null
+++ b/tracking/models/weight_estimated_delivery_date_response.py
@@ -0,0 +1,37 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+from __future__ import annotations
+import pprint
+
+from pydantic import BaseModel
+from typing import Any, Dict, Optional
+from typing_extensions import Self
+
+
+class WeightEstimatedDeliveryDateResponse(BaseModel):
+ """
+ WeightEstimatedDeliveryDateResponse
+ """ # noqa: E501
+
+ unit: Optional[str] = None
+ value: Optional[float] = None
+
+ def to_str(self, **kwargs) -> str:
+ return pprint.pformat(self.model_dump(**kwargs))
+
+ def to_json(self, **kwargs) -> str:
+ return self.model_dump_json(**kwargs)
+
+ def to_dict(self, **kwargs) -> Dict[str, Any]:
+ return self.model_dump(**kwargs)
+
+ @classmethod
+ def from_json(cls, json_str: str, **kwargs) -> Optional[Self]:
+ return cls.model_validate_json(json_str, **kwargs)
+
+ @classmethod
+ def from_dict(cls, obj: Optional[Dict[str, Any]], **kwargs) -> Optional[Self]:
+ return cls.model_validate(obj, **kwargs) if isinstance(obj, Dict) else None
diff --git a/tracking/request.py b/tracking/request.py
new file mode 100644
index 0000000..ac826e6
--- /dev/null
+++ b/tracking/request.py
@@ -0,0 +1,140 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+import random
+from functools import partial, wraps
+from typing import Union, Optional
+from urllib.parse import urljoin
+
+import httpx
+from pydantic import validate_call, ValidationError
+from retrying import retry
+
+from tracking.auth import Authenticator
+from tracking.configuration import Configuration
+from tracking.response import parse_response
+from tracking.exceptions import ApiException, TimedOutError, BadRequestError, ErrorCodeEnum
+
+_default_user_agent = "aftership-sdk-python/2.0.0 (https://www.aftership.com) httpx/0.19.0"
+
+
+def validate_params(func):
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ try:
+ funcx = validate_call(func)
+ return funcx(*args, **kwargs)
+ except ValidationError as e:
+ raise BadRequestError(code=ErrorCodeEnum.INVALID_OPTION, message=e)
+
+ return wrapper
+
+
+class ApiClient:
+ """Generic API client for OpenAPI client library builds.
+
+ :param configuration: .Configuration object for this client
+ """
+
+ _client = httpx.Client()
+
+ def __init__(self, configuration: Optional[Configuration] = None) -> None:
+ self._config = configuration
+
+ if self._config.proxy is not None:
+ self._client = httpx.Client(proxy=self._config.proxy)
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ if self._client is not None:
+ self._client.close()
+
+ def _request(self, method, url, params=None, body=None, **kwargs) -> Union[dict, None]:
+ url = urljoin(self._config.domain, url)
+ user_headers = self._build_headers(body, kwargs.pop("headers", dict()))
+ ssl_ctx = kwargs.pop("verify", None)
+
+ timeout = kwargs.pop("timeout", 0)
+ if timeout <= 0:
+ kwargs["timeout"] = self._config.timeout / 1000.0
+
+ request = self._client.build_request(
+ method=method, url=url, params=params, data=body, headers=user_headers, **kwargs
+ )
+ request.headers = Authenticator(
+ api_key=self._config.api_key,
+ api_secret=self._config.api_secret,
+ auth_type=self._config.authentication_type,
+ ).sign(
+ method=request.method,
+ uri=request.url.raw.raw_path.decode("utf-8"),
+ headers=request.headers,
+ body=body,
+ )
+ return self._send_request_with_retry(ssl_ctx, request)
+
+ def _build_headers(self, body, user_headers) -> dict:
+ _headers = {
+ "aftership-client": _default_user_agent,
+ "user-agent": _default_user_agent,
+ }
+ if self._config.user_agent is not None:
+ _headers["user-agent"] = self._config.user_agent
+
+ if body is not None:
+ _headers["content-type"] = "application/json"
+
+ if "User-Agent" in user_headers:
+ user_headers["user-agent"] = user_headers.pop("User-Agent")
+
+ _headers.update(user_headers)
+ return _headers
+
+ @staticmethod
+ def _retry_if_error(exception):
+ if isinstance(exception, TimedOutError):
+ return True
+
+ if isinstance(exception, ApiException):
+ if exception.status_code >= 500:
+ return True
+ return False
+
+ @staticmethod
+ def _retry_wait(attempts, delay):
+ delay_base = 3000
+ _delay = delay_base * pow(2, attempts - 1)
+ jitter = delay_base * (random.random() - 0.5)
+ return max(1.0, _delay + jitter)
+
+ def _send_request_with_retry(self, ssl_context, request: httpx.Request) -> Union[dict, None]:
+ retry_backoff = partial(
+ retry,
+ wait_func=self._retry_wait,
+ stop_max_attempt_number=self._config.max_retry + 1,
+ retry_on_exception=self._retry_if_error,
+ )
+
+ @retry_backoff()
+ def wrap(ssl_ctx, req):
+ try:
+ if ssl_ctx is None:
+ response = self._client.send(req)
+ else:
+ with httpx.Client(proxy=self._config.proxy, verify=ssl_ctx) as client:
+ response = client.send(req)
+ except httpx.TimeoutException as e:
+ raise TimedOutError(
+ code=ErrorCodeEnum.UNKNOW_ERROR,
+ meta_code=500,
+ status_code=500,
+ message=f"{e.__module__}.{e.__class__.__name__}: {e}",
+ response_body="",
+ )
+ return parse_response(response)
+
+ return wrap(ssl_context, request)
diff --git a/tracking/response.py b/tracking/response.py
new file mode 100644
index 0000000..172fa77
--- /dev/null
+++ b/tracking/response.py
@@ -0,0 +1,94 @@
+# coding: utf-8
+#
+# This code was auto generated by AfterShip SDK Generator.
+# Do not edit the class manually.
+
+import json
+from typing import Union
+
+import httpx
+
+from tracking.exceptions import (
+ ErrorCodeEnum,
+ UnknownError,
+ BadRequestError,
+ UnauthorizedError,
+ ForbiddenError,
+ NotFoundError,
+ InternalError,
+ TooManyRequestsError,
+ TimedOutError,
+)
+
+error_mapping = {
+ "BadRequest": BadRequestError,
+ "Unauthorized": UnauthorizedError,
+ "Forbidden": ForbiddenError,
+ "NotFound": NotFoundError,
+ "TooManyRequests": TooManyRequestsError,
+ "InternalError": InternalError,
+ "TimedOutError": TimedOutError,
+}
+
+_error_meta_code_mapping = {
+ "400": ErrorCodeEnum.INTERNAL_ERROR,
+ "4001": ErrorCodeEnum.INVALID_JSON,
+ "4003": ErrorCodeEnum.TRACKING_ALREADY_EXIST,
+ "4004": ErrorCodeEnum.TRACKING_DOES_NOT_EXIST,
+ "4005": ErrorCodeEnum.TRACKING_NUMBER_INVALID,
+ "4006": ErrorCodeEnum.TRACKING_REQUIRED,
+ "4007": ErrorCodeEnum.TRACKING_NUMBER_REQUIRED,
+ "4008": ErrorCodeEnum.VALUE_INVALID,
+ "4009": ErrorCodeEnum.VALUE_REQUIRED,
+ "4010": ErrorCodeEnum.SLUG_INVALID,
+ "4011": ErrorCodeEnum.MISSING_OR_INVALID_REQUIRED_FIELD,
+ "4012": ErrorCodeEnum.BAD_COURIER,
+ "4013": ErrorCodeEnum.INACTIVE_RETRACK_NOT_ALLOWED,
+ "4014": ErrorCodeEnum.NOTIFICATION_REUQIRED,
+ "4015": ErrorCodeEnum.ID_INVALID,
+ "4016": ErrorCodeEnum.RETRACK_ONCE_ALLOWED,
+ "4017": ErrorCodeEnum.TRACKING_NUMBER_FORMAT_INVALID,
+ "401": ErrorCodeEnum.API_KEY_INVALID,
+ "403": ErrorCodeEnum.REQUEST_NOT_ALLOWED,
+ "404": ErrorCodeEnum.NOT_FOUND,
+ "429": ErrorCodeEnum.TOO_MANY_REQUEST,
+ "500": ErrorCodeEnum.INTERNAL_ERROR,
+ "502": ErrorCodeEnum.INTERNAL_ERROR,
+ "504": ErrorCodeEnum.INTERNAL_ERROR,
+}
+
+
+def get_error_code(meta_code: int) -> str:
+ if _error_meta_code_mapping.get(str(meta_code)):
+ return str(_error_meta_code_mapping[str(meta_code)])
+ return str(ErrorCodeEnum.INTERNAL_ERROR)
+
+
+def parse_response(response: httpx.Response) -> Union[dict, None]:
+ try:
+ json_data = response.json()
+
+ if response.status_code < 300:
+ return json_data["data"]
+
+ error_type = json_data["meta"]["type"]
+ if error_type in error_mapping:
+ error_cls = error_mapping[error_type]
+ else:
+ error_cls = UnknownError
+
+ raise error_cls(
+ code=get_error_code(json_data["meta"]["code"]),
+ meta_code=json_data["meta"]["code"],
+ status_code=response.status_code,
+ message=json_data["meta"]["message"],
+ response_body=response.text,
+ )
+ except (json.JSONDecodeError, KeyError) as e:
+ raise UnknownError(
+ code=ErrorCodeEnum.UNKNOW_ERROR,
+ meta_code=500,
+ status_code=response.status_code,
+ message=f"{e.__module__}.{e.__class__.__name__}: {e}",
+ response_body=response.text,
+ )