SignYu Python SDK
Official Python SDK for the SignYu Aadhaar eSign API. Upload a PDF, add signers, and send it for legally valid Aadhaar OTP based eSignature under the IT Act 2000, all from your backend.
- Python 3.9 or newer, built on
httpx - Fully typed (
py.typed, TypedDict responses) - Webhook signature verification with constant time comparison
API docs: https://signyu.com/docs. API overview and pricing: https://signyu.com/api (signatures from ₹15 each on credit packs of 10 or more).
Install
pip install signyu
Quickstart
from signyu import SignYu
client = SignYu(api_key="sk_live_...") # or set SIGNYU_API_KEY
# 1. Upload the PDF (a path, bytes, or a binary file object)
doc = client.documents.create(file="agreement.pdf", name="Service Agreement")
# 2. Add signers (they sign in this order, up to 6 per document)
client.documents.add_signers(doc["documentId"], [
{"name": "Asha Rao", "phone": "9876543210", "email": "asha@example.com"},
{"name": "Vikram Nair", "phone": "9812345678", "email": "vikram@example.com"},
])
# 3. Send: deducts one credit per signer and emails each signer a link
sent = client.documents.send(doc["documentId"])
print([s["signUrl"] for s in sent["signers"]])
# 4. Check progress (or use webhooks)
status = client.documents.get(doc["documentId"])
print(status["status"]) # PENDING, SENT or COMPLETED
Responses are plain dicts with the API's camelCase keys, typed as TypedDicts in signyu.types.
Get your API key from the dashboard under Developers (https://signyu.com/app/developers). Keep it on your server.
Configuration
client = SignYu(
api_key="sk_live_...", # or the SIGNYU_API_KEY environment variable
base_url="https://signyu.com", # optional
timeout=60.0, # optional, seconds
http_client=None, # optional, your own httpx.Client
)
The client can be used as a context manager (with SignYu(...) as client:) or closed with client.close().
Methods
client.documents.create(file, name=None, file_name=None)
POST /api/v1/documents. Uploads a PDF (at most 10MB) and creates a document in PENDING state. file can be a path (str or pathlib.Path), bytes, or a binary file object. It is always sent as application/pdf. name defaults to the file name.
Returns {"documentId", "name", "status"}.
client.documents.list(limit=None, offset=None)
GET /api/v1/documents. Your documents, most recent first. limit is 1 to 100 (default 20), offset defaults to 0.
Returns {"documents": [{"documentId", "name", "status", "createdAt", "signers": {"total", "signed"}}], "limit", "offset"}.
client.documents.get(document_id)
GET /api/v1/documents/{documentId}. Status and per-signer progress. Once COMPLETED, downloadUrl is a temporary presigned link to the signed PDF and certificateUrl points at the certificate endpoint. Each signer's signUrl is None until the document is sent.
client.documents.add_signers(document_id, signers)
POST /api/v1/documents/{documentId}/signers. Only while the document is PENDING. Each signer needs name, phone (digits only, at least 10) and email. At most 6 signers per document.
Optional custom stamp placement (PDF points, origin at the bottom-left of the page, box at least 140 x 110, one box per page):
client.documents.add_signers(document_id, [{
"name": "Asha Rao",
"phone": "9876543210",
"email": "asha@example.com",
"advanced": {
"signaturePlacement": {
"positions": [{"page": 2, "x": 31, "y": 257, "width": 253, "height": 110}],
},
},
}])
client.documents.send(document_id)
POST /api/v1/documents/{documentId}/send. Deducts one credit per signer, marks the document SENT, emails each signer a signing link and returns the links. Call it once per document.
Returns {"documentId", "status": "SENT", "creditsRemaining", "signers": [{"signerId", "name", "email", "signingOrder", "signUrl"}]}.
client.documents.get_certificate(document_id)
GET /api/v1/documents/{documentId}/certificate. Returns the completion certificate and audit trail PDF as bytes. Only available once the document is COMPLETED, otherwise it raises SignYuError with code invalid_state (409).
with open("certificate.pdf", "wb") as fh:
fh.write(client.documents.get_certificate(document_id))
Webhooks
SignYu sends signer.signed and document.completed events as a JSON POST. Each request carries an X-SignSetu-Signature: sha256=<hex> header, an HMAC-SHA256 of the raw request body keyed with your endpoint's signing secret (whsec_..., shown in the dashboard). Always verify against the raw body bytes.
import os
from flask import Flask, request, abort
import signyu
app = Flask(__name__)
@app.post("/webhooks/signyu")
def signyu_webhook():
try:
event = signyu.webhooks.construct_event(
request.get_data(), # raw bytes
request.headers.get("X-SignSetu-Signature"),
os.environ["SIGNYU_WEBHOOK_SECRET"],
)
except signyu.WebhookSignatureError:
abort(400)
if event["event"] == "document.completed":
... # event["certificateUrl"], event["signers"]
return "", 200
signyu.webhooks.verify_signature(raw_body, header, secret) returns a bool if you prefer to handle it yourself. Deliveries can repeat, so make your handler idempotent.
Errors
Every non-2xx response raises signyu.SignYuError:
from signyu import SignYuError
try:
client.documents.send(document_id)
except SignYuError as err:
print(err.status) # 402
print(err.code) # "insufficient_credits"
print(err.message) # "You need 2 credits to send this document."
| Status | code |
Meaning |
|---|---|---|
| 400 | invalid_content_type, invalid_request, invalid_file, no_signers, signer_limit_reached |
The request is invalid. |
| 401 | unauthorized |
Missing or invalid API key. |
| 402 | insufficient_credits |
Not enough credits to send. |
| 403 | api_access_not_enabled |
The account has no API access subscription. |
| 404 | not_found |
The document does not exist or is not yours. |
| 409 | invalid_state |
Not allowed in the document's current state. |
| 413 | file_too_large |
The PDF is over 10MB. |
| 429 | rate_limited |
Too many requests, retry with backoff. |
| 500 | internal_error |
Something went wrong on our side. |
| 0 | connection_error, timeout |
The request never got a response. |
Retry 429 and 5xx with backoff. Do not blindly retry send, since a successful send that timed out on your side would charge credits again.
Links
- Docs: https://signyu.com/docs
- API: https://signyu.com/api
- OpenAPI spec: https://signyu.com/openapi.yaml
- Support: contact@mail.signyu.com
License
MIT
Metadata
Release files for signyu 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| signyu-0.1.0.tar.gz | 10.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| signyu-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 21.5 kB
Release files / signyu-0.1.0.tar.gz
| Download URL | signyu-0.1.0.tar.gz |
|---|---|
| Size | 10.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
22447fa96e3a2398ce162d514dd4e264f2d25f7e58d53818fd00d86a308781c6
|
|
BLAKE2b-256 checksum How to use checksums |
68bd8c3167ed029159916bbff9b7bfc656e8537155a359133a0f4260f3411844
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.4
|
Release files / signyu-0.1.0-py3-none-any.whl
| Download URL | signyu-0.1.0-py3-none-any.whl |
|---|---|
| Size | 10.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
65d2db05ac1ed3737afe224c943fb47f5d860a9851300248dba69afa73dcb5eb
|
|
BLAKE2b-256 checksum How to use checksums |
c0a9227c49b1a48acfbe9c0f2646203ee74805d98fd8824449e623902bcb8112
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.4
|