Metadata-Version: 2.5
Name: autosignly
Version: 0.1.0
Summary: Python client for the Autosignly API - eIDAS electronic signatures and document workflows
Project-URL: Homepage, https://autosignly.eu
Project-URL: Documentation, https://docs.16it.eu/docs/intro/
Project-URL: Source, https://github.com/16it-pl/autosignly-sdk
Project-URL: Issues, https://github.com/16it-pl/autosignly-sdk/issues
Author: 16it
License-Expression: Apache-2.0
Keywords: autosignly,eidas,electronic-signature,esignature,pades,pdf
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: httpx<1,>=0.27
Description-Content-Type: text/markdown

# autosignly

Python client for the [Autosignly](https://autosignly.eu) API - eIDAS electronic signatures and
document workflows.

> **Not published yet.** This package is being built. Install from source for now.

## Install

```bash
pip install autosignly
```

Requires Python 3.10 or newer.

## Quickstart

```python
from autosignly import AutosignlyClient, Signer

with AutosignlyClient(api_key="api_key_...", api_secret="api_sct_...") as client:
    document_id = client.upload_and_sign(
        pdf=open("contract.pdf", "rb").read(),
        document_name="Consulting agreement",
        signers=[
            Signer(
                first_name="Anna",
                last_name="Nowak",
                email="anna@example.com",
                country="PL",
            )
        ],
    )
    print(document_id)
```

The key and secret decide which environment you are working in. Every environment, production or
sandbox, has its own pair, so pointing a script at the sandbox is a matter of swapping credentials.

The secret must stay on your server. It must never be shipped to a browser or a mobile app.

## Reading documents

```python
document = client.get_document(document_id)
print(document.status, [s.email for s in document.signers])

for summary in client.iter_documents(status="SIGNED"):
    print(summary.id, summary.name)
```

## Downloading the file

A document carries a short-lived link to its file. The link expires, so fetch the document again
for a fresh one rather than storing it.

```python
document = client.get_document(document_id)
print(document.file_url)

pdf = client.download_document(document_id)
open("signed.pdf", "wb").write(pdf)
```

A document that is still being signed can be downloaded as well - it then carries only the
signatures collected so far.

## Tags

```python
tag = client.create_tag("contracts")
client.set_document_tags(document_id, tag_ids=[tag.id], names=["2026"])
```

Setting tags replaces the whole set: tags left out are removed, and names that do not exist yet are
added to the company tag pool.

## Verifying webhooks

Autosignly signs every delivery. Check the signature against the raw request body, before parsing
it - re-serialising the JSON changes the bytes and the signature will not match.

```python
from autosignly import webhooks

webhooks.verify(
    request.body,
    request.headers["X-Webhook-Signature"],
    webhook_key,
    request.headers["X-Webhook-Timestamp"],
)
```

The signature covers the timestamp as well as the body, and a delivery older than five minutes is
rejected even when its signature matches, so a captured request cannot be replayed later.

While a webhook key is being rotated a delivery carries several signatures; it is accepted when any
of them matches, so rotation needs no change on your side.

`verify` raises `InvalidSignatureError` on a mismatch; `webhooks.is_valid(...)` returns a boolean
instead.

## Errors

Every failure raises a subclass of `AutosignlyError` carrying the HTTP status and the error type
returned by the API.

```python
from autosignly import AutosignlyError, NotFoundError

try:
    client.get_document("does-not-exist")
except NotFoundError:
    ...
except AutosignlyError as error:
    print(error.status_code, error.error_type, error.error_id)
```

Connection problems and server errors are retried automatically, with an exponential backoff and
jitter. Client errors are not retried, since repeating a rejected request cannot change its outcome.

Rate limits are retried too, honouring the delay the API asks for. When that delay is longer than a
minute the call fails instead of blocking your thread, and `RateLimitError.retry_after` tells you
how long to wait.

The client does not implement a circuit breaker. It runs inside your process, on calls you asked
for, so refusing to even attempt one would be surprising - and your own infrastructure is the right
place for that policy. Pass your own `http_client` if you want to add one.

## Links

- Website: <https://autosignly.eu>
- API documentation: <https://docs.16it.eu/docs/intro/>
- Source and issues: <https://github.com/16it-pl/autosignly-sdk>

## License

Apache-2.0
