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.
@@ -0,0 +1,6 @@
1
+ node_modules/
2
+ dist/
3
+ __pycache__/
4
+ *.egg-info/
5
+ .pytest_cache/
6
+ .mypy_cache/
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
+ ]