easycontract 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,187 @@
1
+ # Project
2
+ .claude
3
+ .idea
4
+
5
+ # ---> Python
6
+ # Byte-compiled / optimized / DLL files
7
+ __pycache__/
8
+ *.py[cod]
9
+ *$py.class
10
+
11
+ # C extensions
12
+ *.so
13
+
14
+ # Distribution / packaging
15
+ .Python
16
+ build/
17
+ develop-eggs/
18
+ dist/
19
+ downloads/
20
+ eggs/
21
+ .eggs/
22
+ lib/
23
+ lib64/
24
+ parts/
25
+ sdist/
26
+ var/
27
+ wheels/
28
+ share/python-wheels/
29
+ *.egg-info/
30
+ .installed.cfg
31
+ *.egg
32
+ MANIFEST
33
+
34
+ # PyInstaller
35
+ # Usually these files are written by a python script from a template
36
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
37
+ *.manifest
38
+ *.spec
39
+
40
+ # Installer logs
41
+ pip-log.txt
42
+ pip-delete-this-directory.txt
43
+
44
+ # Unit test / coverage reports
45
+ htmlcov/
46
+ .tox/
47
+ .nox/
48
+ .coverage
49
+ .coverage.*
50
+ .cache
51
+ nosetests.xml
52
+ coverage.xml
53
+ *.cover
54
+ *.py,cover
55
+ .hypothesis/
56
+ .pytest_cache/
57
+ cover/
58
+
59
+ # Translations
60
+ *.mo
61
+ *.pot
62
+
63
+ # Django stuff:
64
+ *.log
65
+ local_settings.py
66
+ db.sqlite3
67
+ db.sqlite3-journal
68
+
69
+ # Flask stuff:
70
+ instance/
71
+ .webassets-cache
72
+
73
+ # Scrapy stuff:
74
+ .scrapy
75
+
76
+ # Sphinx documentation
77
+ docs/_build/
78
+
79
+ # PyBuilder
80
+ .pybuilder/
81
+ target/
82
+
83
+ # Jupyter Notebook
84
+ .ipynb_checkpoints
85
+
86
+ # IPython
87
+ profile_default/
88
+ ipython_config.py
89
+
90
+ # pyenv
91
+ # For a library or package, you might want to ignore these files since the code is
92
+ # intended to run in multiple environments; otherwise, check them in:
93
+ # .python-version
94
+
95
+ # pipenv
96
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
97
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
98
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
99
+ # install all needed dependencies.
100
+ #Pipfile.lock
101
+
102
+ # UV
103
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
104
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
105
+ # commonly ignored for libraries.
106
+ #uv.lock
107
+
108
+ # poetry
109
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
110
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
111
+ # commonly ignored for libraries.
112
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
113
+ #poetry.lock
114
+
115
+ # pdm
116
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
117
+ #pdm.lock
118
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
119
+ # in version control.
120
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
121
+ .pdm.toml
122
+ .pdm-python
123
+ .pdm-build/
124
+
125
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
126
+ __pypackages__/
127
+
128
+ # Celery stuff
129
+ celerybeat-schedule*
130
+ celerybeat.pid
131
+
132
+ # SageMath parsed files
133
+ *.sage.py
134
+
135
+ # Environments
136
+ .env
137
+ .venv
138
+ env/
139
+ venv/
140
+ ENV/
141
+ env.bak/
142
+ venv.bak/
143
+
144
+ # Spyder project settings
145
+ .spyderproject
146
+ .spyproject
147
+
148
+ # Rope project settings
149
+ .ropeproject
150
+
151
+ # mkdocs documentation
152
+ /site
153
+
154
+ # mypy
155
+ .mypy_cache/
156
+ .dmypy.json
157
+ dmypy.json
158
+
159
+ # Pyre type checker
160
+ .pyre/
161
+
162
+ # pytype static type analyzer
163
+ .pytype/
164
+
165
+ # Cython debug symbols
166
+ cython_debug/
167
+
168
+ # PyCharm
169
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
170
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
171
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
172
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
173
+ #.idea/
174
+
175
+ # Ruff stuff:
176
+ .ruff_cache/
177
+
178
+ # PyPI configuration file
179
+ .pypirc
180
+
181
+
182
+ # Django project artifacts
183
+ staticfiles/
184
+ media/
185
+
186
+ # CSS build tooling (scripts/build_css.sh downloads pinned binaries here)
187
+ .tools/
@@ -0,0 +1,19 @@
1
+ # Changelog — easycontract Python SDK
2
+
3
+ ## 0.1.0 (unreleased)
4
+
5
+ First public release. Notes for integrators:
6
+
7
+ - `EasyContract.request(..., idempotency_key=...)`: `None` (default) and `True`
8
+ generate a UUID per logical request, reused across retries; `False` sends no
9
+ header; any other value (a `str`, a `uuid.UUID`…) is sent as `str(value)`.
10
+ - Uploads: file objects passed in `files` are closed once the request finishes.
11
+ A non-seekable stream (pipe, socket) is sent once and never retried; pass a
12
+ path, `bytes` or a seekable file to get 429 retries.
13
+ - `webhooks.verify_signature`: `tolerance=0` is strict; only `tolerance=None`
14
+ disables the replay window. Verification runs over the raw bytes and every
15
+ failure raises `SignatureVerificationError`, never a decode/parse error.
16
+ - Auto-pagination raises `EasyContractError` on a page without `data` or on a
17
+ cursor that does not advance, instead of looping or crashing.
18
+ - Runtime dependency pinned to `requests>=2.31,<3`. Licence declared as
19
+ `LicenseRef-easycontract-proprietary` (PEP 639) with `LICENSE.txt`.
@@ -0,0 +1,18 @@
1
+ easycontract Python SDK
2
+
3
+ Copyright (c) 2026 Román Ramírez — roman@easycontract.online
4
+
5
+ Proprietary. All rights reserved.
6
+
7
+ This software and its documentation are proprietary to easycontract. No
8
+ rights are granted to use, modify, reproduce, distribute, or create
9
+ derivative works of this software, except as expressly agreed in writing
10
+ with easycontract (for example, under an applicable customer agreement).
11
+
12
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
17
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
18
+ DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,176 @@
1
+ Metadata-Version: 2.5
2
+ Name: easycontract
3
+ Version: 0.1.0
4
+ Summary: SDK oficial de Python para el API de easycontract (firma electrónica eIDAS)
5
+ Project-URL: Documentation, https://easycontract.online/docs/api
6
+ Author-email: Román Ramírez <roman@easycontract.online>
7
+ License-Expression: LicenseRef-easycontract-proprietary
8
+ License-File: LICENSE.txt
9
+ Keywords: audit-trail,contract,e-signature,eidas,esign,webhooks
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Security
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: requests<3,>=2.31
22
+ Description-Content-Type: text/markdown
23
+
24
+ # easycontract — SDK de Python
25
+
26
+ SDK oficial del API de firma electrónica de easycontract (SES/AdES bajo eIDAS,
27
+ con paquete de evidencias defendible en litigio).
28
+
29
+ ```bash
30
+ pip install easycontract
31
+ ```
32
+
33
+ ```python
34
+ from easycontract import EasyContract
35
+
36
+ client = EasyContract(api_key="ec_test_...") # ec_live_... en producción
37
+ print(client.whoami())
38
+ ```
39
+
40
+ - **Modo test de primera clase**: una clave `ec_test_…` opera sobre datos
41
+ aislados (TSA/PAdES simulados). Nadie debería firmar un documento real para
42
+ probar la integración.
43
+ - **Idempotencia automática**: todo POST lleva `Idempotency-Key` (uuid4);
44
+ pásala tú misma para reintentos seguros de tu lado.
45
+ - **Reintentos**: 429 (respetando `Retry-After`) y 5xx en GETs.
46
+ - **Errores tipados** con el `code` estable del API:
47
+ `AuthenticationError`, `RateLimitError`, `ForbiddenError` (cuota/feature/
48
+ regla de membresía), `InvalidRequestError`, `NotFoundError`,
49
+ `ConflictError`, `ServerError`.
50
+
51
+ ---
52
+
53
+ ## Receta 1 — Enviar un envelope desde plantilla (el flujo RootedCON)
54
+
55
+ Una sola llamada: cubre los roles, envía, y recibe los enlaces de firma.
56
+
57
+ ```python
58
+ result = client.templates.create_envelope(
59
+ template_id,
60
+ signers={"ponente": {"full_name": "Ada Lovelace", "email": "ada@example.com"}},
61
+ title="Contrato ponencia RootedCON 2027 — Ada Lovelace",
62
+ send=True, # congela hashes, plan y retención, y acuña enlaces
63
+ deliver_emails=True, # easycontract envía los emails con los enlaces
64
+ )
65
+ envelope_id = result["envelope"]["id"]
66
+ ```
67
+
68
+ Creación de la plantilla (una vez):
69
+
70
+ ```python
71
+ template = client.templates.create(name="Contrato ponente", retention_period_months=72)
72
+ doc = client.templates.add_document(template["id"], file="contrato-ponente.pdf")
73
+ client.templates.add_role(template["id"], key="ponente", label="Ponente")
74
+ client.templates.add_field(
75
+ template["id"],
76
+ document_id=doc["id"],
77
+ role="ponente",
78
+ anchor_text="Fdo. el ponente:", # la firma se estampa donde el PDF lo dice
79
+ )
80
+ ```
81
+
82
+ ## Receta 2 — Firma embebida (embedded signing)
83
+
84
+ Con `deliver_emails=False` recibes el `sign_url` de un solo uso por firmante y
85
+ lo integras en tu propia aplicación (redirect o iframe):
86
+
87
+ ```python
88
+ result = client.templates.create_envelope(
89
+ template_id,
90
+ signers={...},
91
+ send=True,
92
+ deliver_emails=False,
93
+ )
94
+ sign_url = result["signers"][0]["sign_url"]
95
+ # → redirige al ponente a sign_url sin salir de tu flujo
96
+ ```
97
+
98
+ El enlace es personal, caduca (14 días por defecto, `expires_in_days` para
99
+ cambiarlo) y queda invalidado al firmar o rechazar.
100
+
101
+ ## Receta 3 — Webhooks vs. polling
102
+
103
+ **Webhooks** (recomendado): registra un endpoint y verifica CADA entrega.
104
+
105
+ ```python
106
+ endpoint = client.webhooks.create(url="https://miapp.com/hooks/easycontract")
107
+ WEBHOOK_SECRET = endpoint["secret"] # whsec_… — se muestra UNA sola vez
108
+
109
+ # En tu receptor (Django/Flask/FastAPI):
110
+ from easycontract import webhooks
111
+
112
+ event = webhooks.verify_signature(
113
+ payload=request.body,
114
+ header=request.headers["X-EasyContract-Signature"],
115
+ secret=WEBHOOK_SECRET,
116
+ )
117
+ if event["type"] == "envelope.completed":
118
+ ...
119
+ ```
120
+
121
+ **Reconciliación / polling**: los webhooks son la proyección de un event log
122
+ consultable; si pierdes una entrega, reconcilia:
123
+
124
+ ```python
125
+ for event in client.events.auto_paging_iter(type="envelope.completed"):
126
+ ...
127
+ ```
128
+
129
+ ## Receta 4 — Descargar el paquete de evidencias
130
+
131
+ El ZIP completo (documentos, certificado, audit trail JSON verificable
132
+ offline, sellos TSA) — lo que un tenant llevaría a un juzgado:
133
+
134
+ ```python
135
+ client.envelopes.download_evidence(envelope_id, to="evidencias.zip")
136
+ client.envelopes.download_certificate(envelope_id, to="certificado.pdf")
137
+ ```
138
+
139
+ ## Receta 5 — Gestión de plantillas
140
+
141
+ ```python
142
+ for template in client.templates.list()["data"]:
143
+ print(template["name"])
144
+
145
+ detail = client.templates.get(template_id, expand=["documents", "roles", "fields"])
146
+ client.templates.delete(old_template_id)
147
+ ```
148
+
149
+ ---
150
+
151
+ ## Envelope manual (sin plantilla)
152
+
153
+ ```python
154
+ envelope = client.envelopes.create(
155
+ title="NDA",
156
+ retention_period_months=60, # retención SIEMPRE explícita
157
+ )
158
+ client.envelopes.add_document(envelope["id"], file="nda.pdf")
159
+ client.envelopes.add_signer(
160
+ envelope["id"],
161
+ full_name="Grace Hopper",
162
+ email="grace@example.com",
163
+ auth_methods=["email_link", "sms_otp"],
164
+ phone="+34600111222", # OTP: plan Pro+
165
+ )
166
+ sent = client.envelopes.send(envelope["id"])
167
+ ```
168
+
169
+ ## Organización, miembros e invitaciones
170
+
171
+ ```python
172
+ org = client.organization.get() # plan, límites y features del plan
173
+ members = client.members.list()
174
+ invite = client.invitations.create(email="colega@example.com", role="member")
175
+ # invite["accept_url"] — entrégalo tú o deja que easycontract lo envíe por email
176
+ ```
@@ -0,0 +1,153 @@
1
+ # easycontract — SDK de Python
2
+
3
+ SDK oficial del API de firma electrónica de easycontract (SES/AdES bajo eIDAS,
4
+ con paquete de evidencias defendible en litigio).
5
+
6
+ ```bash
7
+ pip install easycontract
8
+ ```
9
+
10
+ ```python
11
+ from easycontract import EasyContract
12
+
13
+ client = EasyContract(api_key="ec_test_...") # ec_live_... en producción
14
+ print(client.whoami())
15
+ ```
16
+
17
+ - **Modo test de primera clase**: una clave `ec_test_…` opera sobre datos
18
+ aislados (TSA/PAdES simulados). Nadie debería firmar un documento real para
19
+ probar la integración.
20
+ - **Idempotencia automática**: todo POST lleva `Idempotency-Key` (uuid4);
21
+ pásala tú misma para reintentos seguros de tu lado.
22
+ - **Reintentos**: 429 (respetando `Retry-After`) y 5xx en GETs.
23
+ - **Errores tipados** con el `code` estable del API:
24
+ `AuthenticationError`, `RateLimitError`, `ForbiddenError` (cuota/feature/
25
+ regla de membresía), `InvalidRequestError`, `NotFoundError`,
26
+ `ConflictError`, `ServerError`.
27
+
28
+ ---
29
+
30
+ ## Receta 1 — Enviar un envelope desde plantilla (el flujo RootedCON)
31
+
32
+ Una sola llamada: cubre los roles, envía, y recibe los enlaces de firma.
33
+
34
+ ```python
35
+ result = client.templates.create_envelope(
36
+ template_id,
37
+ signers={"ponente": {"full_name": "Ada Lovelace", "email": "ada@example.com"}},
38
+ title="Contrato ponencia RootedCON 2027 — Ada Lovelace",
39
+ send=True, # congela hashes, plan y retención, y acuña enlaces
40
+ deliver_emails=True, # easycontract envía los emails con los enlaces
41
+ )
42
+ envelope_id = result["envelope"]["id"]
43
+ ```
44
+
45
+ Creación de la plantilla (una vez):
46
+
47
+ ```python
48
+ template = client.templates.create(name="Contrato ponente", retention_period_months=72)
49
+ doc = client.templates.add_document(template["id"], file="contrato-ponente.pdf")
50
+ client.templates.add_role(template["id"], key="ponente", label="Ponente")
51
+ client.templates.add_field(
52
+ template["id"],
53
+ document_id=doc["id"],
54
+ role="ponente",
55
+ anchor_text="Fdo. el ponente:", # la firma se estampa donde el PDF lo dice
56
+ )
57
+ ```
58
+
59
+ ## Receta 2 — Firma embebida (embedded signing)
60
+
61
+ Con `deliver_emails=False` recibes el `sign_url` de un solo uso por firmante y
62
+ lo integras en tu propia aplicación (redirect o iframe):
63
+
64
+ ```python
65
+ result = client.templates.create_envelope(
66
+ template_id,
67
+ signers={...},
68
+ send=True,
69
+ deliver_emails=False,
70
+ )
71
+ sign_url = result["signers"][0]["sign_url"]
72
+ # → redirige al ponente a sign_url sin salir de tu flujo
73
+ ```
74
+
75
+ El enlace es personal, caduca (14 días por defecto, `expires_in_days` para
76
+ cambiarlo) y queda invalidado al firmar o rechazar.
77
+
78
+ ## Receta 3 — Webhooks vs. polling
79
+
80
+ **Webhooks** (recomendado): registra un endpoint y verifica CADA entrega.
81
+
82
+ ```python
83
+ endpoint = client.webhooks.create(url="https://miapp.com/hooks/easycontract")
84
+ WEBHOOK_SECRET = endpoint["secret"] # whsec_… — se muestra UNA sola vez
85
+
86
+ # En tu receptor (Django/Flask/FastAPI):
87
+ from easycontract import webhooks
88
+
89
+ event = webhooks.verify_signature(
90
+ payload=request.body,
91
+ header=request.headers["X-EasyContract-Signature"],
92
+ secret=WEBHOOK_SECRET,
93
+ )
94
+ if event["type"] == "envelope.completed":
95
+ ...
96
+ ```
97
+
98
+ **Reconciliación / polling**: los webhooks son la proyección de un event log
99
+ consultable; si pierdes una entrega, reconcilia:
100
+
101
+ ```python
102
+ for event in client.events.auto_paging_iter(type="envelope.completed"):
103
+ ...
104
+ ```
105
+
106
+ ## Receta 4 — Descargar el paquete de evidencias
107
+
108
+ El ZIP completo (documentos, certificado, audit trail JSON verificable
109
+ offline, sellos TSA) — lo que un tenant llevaría a un juzgado:
110
+
111
+ ```python
112
+ client.envelopes.download_evidence(envelope_id, to="evidencias.zip")
113
+ client.envelopes.download_certificate(envelope_id, to="certificado.pdf")
114
+ ```
115
+
116
+ ## Receta 5 — Gestión de plantillas
117
+
118
+ ```python
119
+ for template in client.templates.list()["data"]:
120
+ print(template["name"])
121
+
122
+ detail = client.templates.get(template_id, expand=["documents", "roles", "fields"])
123
+ client.templates.delete(old_template_id)
124
+ ```
125
+
126
+ ---
127
+
128
+ ## Envelope manual (sin plantilla)
129
+
130
+ ```python
131
+ envelope = client.envelopes.create(
132
+ title="NDA",
133
+ retention_period_months=60, # retención SIEMPRE explícita
134
+ )
135
+ client.envelopes.add_document(envelope["id"], file="nda.pdf")
136
+ client.envelopes.add_signer(
137
+ envelope["id"],
138
+ full_name="Grace Hopper",
139
+ email="grace@example.com",
140
+ auth_methods=["email_link", "sms_otp"],
141
+ phone="+34600111222", # OTP: plan Pro+
142
+ )
143
+ sent = client.envelopes.send(envelope["id"])
144
+ ```
145
+
146
+ ## Organización, miembros e invitaciones
147
+
148
+ ```python
149
+ org = client.organization.get() # plan, límites y features del plan
150
+ members = client.members.list()
151
+ invite = client.invitations.create(email="colega@example.com", role="member")
152
+ # invite["accept_url"] — entrégalo tú o deja que easycontract lo envíe por email
153
+ ```
@@ -0,0 +1,39 @@
1
+ [project]
2
+ name = "easycontract"
3
+ version = "0.1.0"
4
+ description = "SDK oficial de Python para el API de easycontract (firma electrónica eIDAS)"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = "LicenseRef-easycontract-proprietary"
8
+ authors = [{ name = "Román Ramírez", email = "roman@easycontract.online" }]
9
+ keywords = ["e-signature", "eidas", "esign", "contract", "webhooks", "audit-trail"]
10
+ classifiers = [
11
+ "Development Status :: 4 - Beta",
12
+ "Intended Audience :: Developers",
13
+ "Operating System :: OS Independent",
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.10",
16
+ "Programming Language :: Python :: 3.11",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Topic :: Security",
20
+ "Topic :: Software Development :: Libraries :: Python Modules",
21
+ ]
22
+ dependencies = [
23
+ "requests>=2.31,<3",
24
+ ]
25
+
26
+ [project.urls]
27
+ Documentation = "https://easycontract.online/docs/api"
28
+
29
+ [build-system]
30
+ requires = ["hatchling"]
31
+ build-backend = "hatchling.build"
32
+
33
+ [tool.hatch.build.targets.wheel]
34
+ packages = ["src/easycontract"]
35
+
36
+ [tool.hatch.build.targets.sdist]
37
+ # Internal material never ships in the sdist. (hatchling still adds the
38
+ # repo-root .gitignore to the sdist; it holds nothing sensitive.)
39
+ exclude = ["SECURITY-AUDIT*.md", "dist/"]
@@ -0,0 +1,40 @@
1
+ """easycontract — SDK oficial de Python.
2
+
3
+ from easycontract import EasyContract
4
+
5
+ client = EasyContract(api_key="ec_test_...")
6
+ envelope = client.envelopes.create(title="Contrato", retention_period_months=72)
7
+ """
8
+
9
+ __version__ = "0.1.0"
10
+
11
+ from . import webhooks
12
+ from .client import EasyContract
13
+ from .errors import (
14
+ APIError,
15
+ AuthenticationError,
16
+ ConflictError,
17
+ EasyContractError,
18
+ ForbiddenError,
19
+ InvalidRequestError,
20
+ NotFoundError,
21
+ RateLimitError,
22
+ ServerError,
23
+ SignatureVerificationError,
24
+ )
25
+
26
+ __all__ = [
27
+ "EasyContract",
28
+ "EasyContractError",
29
+ "APIError",
30
+ "AuthenticationError",
31
+ "ForbiddenError",
32
+ "NotFoundError",
33
+ "InvalidRequestError",
34
+ "ConflictError",
35
+ "RateLimitError",
36
+ "ServerError",
37
+ "SignatureVerificationError",
38
+ "webhooks",
39
+ "__version__",
40
+ ]