Metadata-Version: 2.5
Name: signyu
Version: 0.1.0
Summary: Official Python SDK for the SignYu Aadhaar eSign API
Project-URL: Homepage, https://signyu.com/api
Project-URL: Documentation, https://signyu.com/docs
Project-URL: Bug Tracker, https://signyu.com/contact
Author-email: SignYu <contact@mail.signyu.com>
License-Expression: MIT
License-File: LICENSE
Keywords: aadhaar,aadhaar-esign,digital-signature,e-signature,emudhra,esign,india,signyu
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx<1,>=0.24
Requires-Dist: typing-extensions>=4.5; python_version < '3.11'
Provides-Extra: test
Requires-Dist: pytest>=7; extra == 'test'
Description-Content-Type: text/markdown

# SignYu Python SDK

Official Python SDK for the [SignYu](https://signyu.com) 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

```bash
pip install signyu
```

## Quickstart

```python
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 `TypedDict`s in `signyu.types`.

Get your API key from the dashboard under Developers (https://signyu.com/app/developers). Keep it on your server.

## Configuration

```python
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):

```python
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).

```python
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.

```python
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`:

```python
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
