signyt 0.2.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.
- signyt-0.2.0/.gitignore +6 -0
- signyt-0.2.0/PKG-INFO +88 -0
- signyt-0.2.0/README.md +72 -0
- signyt-0.2.0/pyproject.toml +27 -0
- signyt-0.2.0/src/signyt/__init__.py +27 -0
- signyt-0.2.0/src/signyt/_models.py +1139 -0
- signyt-0.2.0/src/signyt/_ops.py +370 -0
- signyt-0.2.0/src/signyt/client.py +633 -0
- signyt-0.2.0/src/signyt/errors.py +101 -0
- signyt-0.2.0/src/signyt/py.typed +0 -0
- signyt-0.2.0/src/signyt/types.py +36 -0
- signyt-0.2.0/src/signyt/webhooks.py +59 -0
- signyt-0.2.0/tests/test_client.py +273 -0
- signyt-0.2.0/tests/test_spec_paths.py +44 -0
- signyt-0.2.0/tests/test_webhooks.py +70 -0
signyt-0.2.0/.gitignore
ADDED
signyt-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: signyt
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Official Python SDK for the sign.yt e-signature API
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Classifier: Typing :: Typed
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: httpx>=0.27
|
|
9
|
+
Requires-Dist: pydantic>=2.6
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
12
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
13
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
14
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# signyt (Python)
|
|
18
|
+
|
|
19
|
+
Typed sync + async client for the [sign.yt](https://sign.yt) REST API. Python 3.10+, depends on `httpx` and `pydantic` v2.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install signyt
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quickstart
|
|
26
|
+
|
|
27
|
+
Create an API key in the app (`/app/api-keys`) with `documents:read` + `documents:write` (and `webhooks:manage` for webhooks).
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from signyt import Signyt
|
|
31
|
+
|
|
32
|
+
with Signyt("sk_live_...", base_url="https://sign.yt/api/v1") as s:
|
|
33
|
+
# 1. create a draft from a PDF, with a signer
|
|
34
|
+
doc = s.documents.create(
|
|
35
|
+
"contract.pdf",
|
|
36
|
+
title="Service agreement",
|
|
37
|
+
recipients=[{"email": "jan@acme.pl", "name": "Jan Kowalski"}],
|
|
38
|
+
verification_type="EMAIL_OTP",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# 2. place a signature field for that signer (positions are % of the page)
|
|
42
|
+
s.documents.set_fields(doc.id, [{
|
|
43
|
+
"type": "SIGNATURE", "page": 1, "position_x": 15, "position_y": 80,
|
|
44
|
+
"width": 25, "height": 8, "recipient_id": doc.recipients[0].id,
|
|
45
|
+
}])
|
|
46
|
+
|
|
47
|
+
# 3. send
|
|
48
|
+
s.documents.send(doc.id)
|
|
49
|
+
|
|
50
|
+
# 4. subscribe once; then wait for the webhook (below) instead of polling
|
|
51
|
+
s.webhooks.create("https://yourapp.com/hooks/signyt", ["document.completed"])
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Async: `async with AsyncSignyt("sk_live_...") as s: await s.documents.send(id)`.
|
|
55
|
+
|
|
56
|
+
### Receive the webhook
|
|
57
|
+
|
|
58
|
+
Deliveries carry `X-Signyt-Signature: sha256=<hex>` and `X-Signyt-Timestamp`; the HMAC input is
|
|
59
|
+
`<timestamp>.<raw body>`. Pass the **raw** body bytes:
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from signyt.webhooks import construct_event # or verify_signature(payload, sig, secret, ts)
|
|
63
|
+
|
|
64
|
+
@app.post("/hooks/signyt")
|
|
65
|
+
async def hook(request: Request):
|
|
66
|
+
event = construct_event(await request.body(), request.headers, WEBHOOK_SECRET)
|
|
67
|
+
if event["event"] == "document.completed":
|
|
68
|
+
pdf = s.documents.download_signed_pdf(event["data"]["document_id"])
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`SignatureVerificationError` is raised on a bad signature or a timestamp older than `tolerance` (default 300 s).
|
|
72
|
+
|
|
73
|
+
## Errors and retries
|
|
74
|
+
|
|
75
|
+
All errors derive from `SignytError` (`.status`, `.code`, `.message`, `.extra`, `.request_id`):
|
|
76
|
+
`AuthenticationError` 401, `PaymentRequiredError` 402, `PermissionDeniedError` 403, `NotFoundError` 404,
|
|
77
|
+
`BadRequestError` 400/413/422, `RateLimitError` 429 (`.retry_after`), `ServerError` 5xx, `APIConnectionError`.
|
|
78
|
+
|
|
79
|
+
`GET`/`PUT`/`DELETE` are retried on 429, 5xx and network errors (`max_retries=2`, honours `Retry-After`,
|
|
80
|
+
else exponential backoff). `POST`/`PATCH` are never retried automatically.
|
|
81
|
+
|
|
82
|
+
## Resources
|
|
83
|
+
|
|
84
|
+
`documents`: list, get, create, set_fields, detect_fields, update_recipient, resend, send, cancel, remind,
|
|
85
|
+
delete, restore, download_signed_pdf, download_original_pdf, audit.
|
|
86
|
+
`templates`: list, create, use, delete. `webhooks`: list, create, update, delete.
|
|
87
|
+
|
|
88
|
+
Response models live in `signyt.models` and are generated from the OpenAPI spec (`make sdk.generate`).
|
signyt-0.2.0/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# signyt (Python)
|
|
2
|
+
|
|
3
|
+
Typed sync + async client for the [sign.yt](https://sign.yt) REST API. Python 3.10+, depends on `httpx` and `pydantic` v2.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install signyt
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Quickstart
|
|
10
|
+
|
|
11
|
+
Create an API key in the app (`/app/api-keys`) with `documents:read` + `documents:write` (and `webhooks:manage` for webhooks).
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from signyt import Signyt
|
|
15
|
+
|
|
16
|
+
with Signyt("sk_live_...", base_url="https://sign.yt/api/v1") as s:
|
|
17
|
+
# 1. create a draft from a PDF, with a signer
|
|
18
|
+
doc = s.documents.create(
|
|
19
|
+
"contract.pdf",
|
|
20
|
+
title="Service agreement",
|
|
21
|
+
recipients=[{"email": "jan@acme.pl", "name": "Jan Kowalski"}],
|
|
22
|
+
verification_type="EMAIL_OTP",
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# 2. place a signature field for that signer (positions are % of the page)
|
|
26
|
+
s.documents.set_fields(doc.id, [{
|
|
27
|
+
"type": "SIGNATURE", "page": 1, "position_x": 15, "position_y": 80,
|
|
28
|
+
"width": 25, "height": 8, "recipient_id": doc.recipients[0].id,
|
|
29
|
+
}])
|
|
30
|
+
|
|
31
|
+
# 3. send
|
|
32
|
+
s.documents.send(doc.id)
|
|
33
|
+
|
|
34
|
+
# 4. subscribe once; then wait for the webhook (below) instead of polling
|
|
35
|
+
s.webhooks.create("https://yourapp.com/hooks/signyt", ["document.completed"])
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Async: `async with AsyncSignyt("sk_live_...") as s: await s.documents.send(id)`.
|
|
39
|
+
|
|
40
|
+
### Receive the webhook
|
|
41
|
+
|
|
42
|
+
Deliveries carry `X-Signyt-Signature: sha256=<hex>` and `X-Signyt-Timestamp`; the HMAC input is
|
|
43
|
+
`<timestamp>.<raw body>`. Pass the **raw** body bytes:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from signyt.webhooks import construct_event # or verify_signature(payload, sig, secret, ts)
|
|
47
|
+
|
|
48
|
+
@app.post("/hooks/signyt")
|
|
49
|
+
async def hook(request: Request):
|
|
50
|
+
event = construct_event(await request.body(), request.headers, WEBHOOK_SECRET)
|
|
51
|
+
if event["event"] == "document.completed":
|
|
52
|
+
pdf = s.documents.download_signed_pdf(event["data"]["document_id"])
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`SignatureVerificationError` is raised on a bad signature or a timestamp older than `tolerance` (default 300 s).
|
|
56
|
+
|
|
57
|
+
## Errors and retries
|
|
58
|
+
|
|
59
|
+
All errors derive from `SignytError` (`.status`, `.code`, `.message`, `.extra`, `.request_id`):
|
|
60
|
+
`AuthenticationError` 401, `PaymentRequiredError` 402, `PermissionDeniedError` 403, `NotFoundError` 404,
|
|
61
|
+
`BadRequestError` 400/413/422, `RateLimitError` 429 (`.retry_after`), `ServerError` 5xx, `APIConnectionError`.
|
|
62
|
+
|
|
63
|
+
`GET`/`PUT`/`DELETE` are retried on 429, 5xx and network errors (`max_retries=2`, honours `Retry-After`,
|
|
64
|
+
else exponential backoff). `POST`/`PATCH` are never retried automatically.
|
|
65
|
+
|
|
66
|
+
## Resources
|
|
67
|
+
|
|
68
|
+
`documents`: list, get, create, set_fields, detect_fields, update_recipient, resend, send, cancel, remind,
|
|
69
|
+
delete, restore, download_signed_pdf, download_original_pdf, audit.
|
|
70
|
+
`templates`: list, create, use, delete. `webhooks`: list, create, update, delete.
|
|
71
|
+
|
|
72
|
+
Response models live in `signyt.models` and are generated from the OpenAPI spec (`make sdk.generate`).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "signyt"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Official Python SDK for the sign.yt e-signature API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
dependencies = ["httpx>=0.27", "pydantic>=2.6"]
|
|
13
|
+
classifiers = ["Typing :: Typed"]
|
|
14
|
+
|
|
15
|
+
[project.optional-dependencies]
|
|
16
|
+
dev = ["pytest>=8", "pytest-asyncio>=0.23", "respx>=0.21", "mypy>=1.10"]
|
|
17
|
+
|
|
18
|
+
[tool.hatch.build.targets.wheel]
|
|
19
|
+
packages = ["src/signyt"]
|
|
20
|
+
|
|
21
|
+
[tool.pytest.ini_options]
|
|
22
|
+
asyncio_mode = "auto"
|
|
23
|
+
testpaths = ["tests"]
|
|
24
|
+
|
|
25
|
+
[tool.mypy]
|
|
26
|
+
strict = true
|
|
27
|
+
exclude = ["_models\\.py"]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""sign.yt Python SDK."""
|
|
2
|
+
|
|
3
|
+
from . import _models as models
|
|
4
|
+
from . import webhooks
|
|
5
|
+
from .client import AsyncSignyt, Signyt
|
|
6
|
+
from .errors import (
|
|
7
|
+
APIConnectionError,
|
|
8
|
+
AuthenticationError,
|
|
9
|
+
BadRequestError,
|
|
10
|
+
ConflictError,
|
|
11
|
+
NotFoundError,
|
|
12
|
+
PaymentRequiredError,
|
|
13
|
+
PermissionDeniedError,
|
|
14
|
+
RateLimitError,
|
|
15
|
+
ServerError,
|
|
16
|
+
SignatureVerificationError,
|
|
17
|
+
SignytError,
|
|
18
|
+
)
|
|
19
|
+
from .types import FieldInput, RecipientInput
|
|
20
|
+
|
|
21
|
+
__version__ = "0.2.0"
|
|
22
|
+
__all__ = [
|
|
23
|
+
"Signyt", "AsyncSignyt", "models", "webhooks", "RecipientInput", "FieldInput",
|
|
24
|
+
"SignytError", "APIConnectionError", "AuthenticationError", "BadRequestError",
|
|
25
|
+
"ConflictError", "NotFoundError", "PaymentRequiredError", "PermissionDeniedError",
|
|
26
|
+
"RateLimitError", "ServerError", "SignatureVerificationError",
|
|
27
|
+
]
|