autosignly 0.1.0.dev0__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.
- autosignly-0.1.0.dev0/.gitignore +33 -0
- autosignly-0.1.0.dev0/PKG-INFO +147 -0
- autosignly-0.1.0.dev0/README.md +127 -0
- autosignly-0.1.0.dev0/pyproject.toml +36 -0
- autosignly-0.1.0.dev0/src/autosignly/__init__.py +70 -0
- autosignly-0.1.0.dev0/src/autosignly/_version.py +1 -0
- autosignly-0.1.0.dev0/src/autosignly/client.py +401 -0
- autosignly-0.1.0.dev0/src/autosignly/errors.py +69 -0
- autosignly-0.1.0.dev0/src/autosignly/models.py +242 -0
- autosignly-0.1.0.dev0/src/autosignly/webhooks.py +39 -0
- autosignly-0.1.0.dev0/tests/test_client.py +312 -0
- autosignly-0.1.0.dev0/tests/test_webhooks.py +40 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# IDE
|
|
2
|
+
.idea/
|
|
3
|
+
.vscode/
|
|
4
|
+
*.iml
|
|
5
|
+
|
|
6
|
+
# OS
|
|
7
|
+
.DS_Store
|
|
8
|
+
Thumbs.db
|
|
9
|
+
|
|
10
|
+
# Secrets and local configuration
|
|
11
|
+
.env
|
|
12
|
+
.env.local
|
|
13
|
+
*.local
|
|
14
|
+
|
|
15
|
+
# Java
|
|
16
|
+
target/
|
|
17
|
+
build/
|
|
18
|
+
.gradle/
|
|
19
|
+
|
|
20
|
+
# Node
|
|
21
|
+
node_modules/
|
|
22
|
+
dist/
|
|
23
|
+
*.tsbuildinfo
|
|
24
|
+
npm-debug.log*
|
|
25
|
+
|
|
26
|
+
# Python
|
|
27
|
+
__pycache__/
|
|
28
|
+
*.py[cod]
|
|
29
|
+
.venv/
|
|
30
|
+
*.egg-info/
|
|
31
|
+
|
|
32
|
+
# uv lock is not versioned for a library - consumers resolve their own dependencies
|
|
33
|
+
uv.lock
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: autosignly
|
|
3
|
+
Version: 0.1.0.dev0
|
|
4
|
+
Summary: Python client for the Autosignly API - eIDAS electronic signatures and document workflows
|
|
5
|
+
Project-URL: Homepage, https://autosignly.eu
|
|
6
|
+
Project-URL: Documentation, https://docs.16it.eu/docs/intro/
|
|
7
|
+
Project-URL: Source, https://github.com/16it-pl/autosignly-sdk
|
|
8
|
+
Project-URL: Issues, https://github.com/16it-pl/autosignly-sdk/issues
|
|
9
|
+
Author: 16it
|
|
10
|
+
License-Expression: Apache-2.0
|
|
11
|
+
Keywords: autosignly,eidas,electronic-signature,esignature,pades,pdf
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Requires-Dist: httpx<1,>=0.27
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# autosignly
|
|
22
|
+
|
|
23
|
+
Python client for the [Autosignly](https://autosignly.eu) API - eIDAS electronic signatures and
|
|
24
|
+
document workflows.
|
|
25
|
+
|
|
26
|
+
> **Not published yet.** This package is being built. Install from source for now.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install autosignly
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Requires Python 3.10 or newer.
|
|
35
|
+
|
|
36
|
+
## Quickstart
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from autosignly import AutosignlyClient, Signer
|
|
40
|
+
|
|
41
|
+
with AutosignlyClient(api_key="api_key_...", api_secret="api_sct_...") as client:
|
|
42
|
+
document_id = client.upload_and_sign(
|
|
43
|
+
pdf=open("contract.pdf", "rb").read(),
|
|
44
|
+
document_name="Consulting agreement",
|
|
45
|
+
signers=[
|
|
46
|
+
Signer(
|
|
47
|
+
first_name="Anna",
|
|
48
|
+
last_name="Nowak",
|
|
49
|
+
email="anna@example.com",
|
|
50
|
+
country="PL",
|
|
51
|
+
)
|
|
52
|
+
],
|
|
53
|
+
)
|
|
54
|
+
print(document_id)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The key and secret decide which environment you are working in. Every environment, production or
|
|
58
|
+
sandbox, has its own pair, so pointing a script at the sandbox is a matter of swapping credentials.
|
|
59
|
+
|
|
60
|
+
The secret must stay on your server. It must never be shipped to a browser or a mobile app.
|
|
61
|
+
|
|
62
|
+
## Reading documents
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
document = client.get_document(document_id)
|
|
66
|
+
print(document.status, [s.email for s in document.signers])
|
|
67
|
+
|
|
68
|
+
for summary in client.iter_documents(status="SIGNED"):
|
|
69
|
+
print(summary.id, summary.name)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Downloading the file
|
|
73
|
+
|
|
74
|
+
A document carries a short-lived link to its file. The link expires, so fetch the document again
|
|
75
|
+
for a fresh one rather than storing it.
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
document = client.get_document(document_id)
|
|
79
|
+
print(document.file_url)
|
|
80
|
+
|
|
81
|
+
pdf = client.download_document(document_id)
|
|
82
|
+
open("signed.pdf", "wb").write(pdf)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
A document that is still being signed can be downloaded as well - it then carries only the
|
|
86
|
+
signatures collected so far.
|
|
87
|
+
|
|
88
|
+
## Tags
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
tag = client.create_tag("contracts")
|
|
92
|
+
client.set_document_tags(document_id, tag_ids=[tag.id], names=["2026"])
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Setting tags replaces the whole set: tags left out are removed, and names that do not exist yet are
|
|
96
|
+
added to the company tag pool.
|
|
97
|
+
|
|
98
|
+
## Verifying webhooks
|
|
99
|
+
|
|
100
|
+
Autosignly signs every webhook delivery. Check the signature against the raw request body, before
|
|
101
|
+
parsing it - re-serialising the JSON changes the bytes and the signature will not match.
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from autosignly import webhooks
|
|
105
|
+
|
|
106
|
+
webhooks.verify(request.body, request.headers["X-Webhook-Signature"], webhook_key)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`verify` raises `InvalidSignatureError` on a mismatch; `webhooks.is_valid(...)` returns a boolean
|
|
110
|
+
instead.
|
|
111
|
+
|
|
112
|
+
## Errors
|
|
113
|
+
|
|
114
|
+
Every failure raises a subclass of `AutosignlyError` carrying the HTTP status and the error type
|
|
115
|
+
returned by the API.
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
from autosignly import AutosignlyError, NotFoundError
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
client.get_document("does-not-exist")
|
|
122
|
+
except NotFoundError:
|
|
123
|
+
...
|
|
124
|
+
except AutosignlyError as error:
|
|
125
|
+
print(error.status_code, error.error_type, error.error_id)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Connection problems and server errors are retried automatically, with an exponential backoff and
|
|
129
|
+
jitter. Client errors are not retried, since repeating a rejected request cannot change its outcome.
|
|
130
|
+
|
|
131
|
+
Rate limits are retried too, honouring the delay the API asks for. When that delay is longer than a
|
|
132
|
+
minute the call fails instead of blocking your thread, and `RateLimitError.retry_after` tells you
|
|
133
|
+
how long to wait.
|
|
134
|
+
|
|
135
|
+
The client does not implement a circuit breaker. It runs inside your process, on calls you asked
|
|
136
|
+
for, so refusing to even attempt one would be surprising - and your own infrastructure is the right
|
|
137
|
+
place for that policy. Pass your own `http_client` if you want to add one.
|
|
138
|
+
|
|
139
|
+
## Links
|
|
140
|
+
|
|
141
|
+
- Website: <https://autosignly.eu>
|
|
142
|
+
- API documentation: <https://docs.16it.eu/docs/intro/>
|
|
143
|
+
- Source and issues: <https://github.com/16it-pl/autosignly-sdk>
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
Apache-2.0
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# autosignly
|
|
2
|
+
|
|
3
|
+
Python client for the [Autosignly](https://autosignly.eu) API - eIDAS electronic signatures and
|
|
4
|
+
document workflows.
|
|
5
|
+
|
|
6
|
+
> **Not published yet.** This package is being built. Install from source for now.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install autosignly
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Requires Python 3.10 or newer.
|
|
15
|
+
|
|
16
|
+
## Quickstart
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from autosignly import AutosignlyClient, Signer
|
|
20
|
+
|
|
21
|
+
with AutosignlyClient(api_key="api_key_...", api_secret="api_sct_...") as client:
|
|
22
|
+
document_id = client.upload_and_sign(
|
|
23
|
+
pdf=open("contract.pdf", "rb").read(),
|
|
24
|
+
document_name="Consulting agreement",
|
|
25
|
+
signers=[
|
|
26
|
+
Signer(
|
|
27
|
+
first_name="Anna",
|
|
28
|
+
last_name="Nowak",
|
|
29
|
+
email="anna@example.com",
|
|
30
|
+
country="PL",
|
|
31
|
+
)
|
|
32
|
+
],
|
|
33
|
+
)
|
|
34
|
+
print(document_id)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The key and secret decide which environment you are working in. Every environment, production or
|
|
38
|
+
sandbox, has its own pair, so pointing a script at the sandbox is a matter of swapping credentials.
|
|
39
|
+
|
|
40
|
+
The secret must stay on your server. It must never be shipped to a browser or a mobile app.
|
|
41
|
+
|
|
42
|
+
## Reading documents
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
document = client.get_document(document_id)
|
|
46
|
+
print(document.status, [s.email for s in document.signers])
|
|
47
|
+
|
|
48
|
+
for summary in client.iter_documents(status="SIGNED"):
|
|
49
|
+
print(summary.id, summary.name)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Downloading the file
|
|
53
|
+
|
|
54
|
+
A document carries a short-lived link to its file. The link expires, so fetch the document again
|
|
55
|
+
for a fresh one rather than storing it.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
document = client.get_document(document_id)
|
|
59
|
+
print(document.file_url)
|
|
60
|
+
|
|
61
|
+
pdf = client.download_document(document_id)
|
|
62
|
+
open("signed.pdf", "wb").write(pdf)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
A document that is still being signed can be downloaded as well - it then carries only the
|
|
66
|
+
signatures collected so far.
|
|
67
|
+
|
|
68
|
+
## Tags
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
tag = client.create_tag("contracts")
|
|
72
|
+
client.set_document_tags(document_id, tag_ids=[tag.id], names=["2026"])
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Setting tags replaces the whole set: tags left out are removed, and names that do not exist yet are
|
|
76
|
+
added to the company tag pool.
|
|
77
|
+
|
|
78
|
+
## Verifying webhooks
|
|
79
|
+
|
|
80
|
+
Autosignly signs every webhook delivery. Check the signature against the raw request body, before
|
|
81
|
+
parsing it - re-serialising the JSON changes the bytes and the signature will not match.
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from autosignly import webhooks
|
|
85
|
+
|
|
86
|
+
webhooks.verify(request.body, request.headers["X-Webhook-Signature"], webhook_key)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`verify` raises `InvalidSignatureError` on a mismatch; `webhooks.is_valid(...)` returns a boolean
|
|
90
|
+
instead.
|
|
91
|
+
|
|
92
|
+
## Errors
|
|
93
|
+
|
|
94
|
+
Every failure raises a subclass of `AutosignlyError` carrying the HTTP status and the error type
|
|
95
|
+
returned by the API.
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from autosignly import AutosignlyError, NotFoundError
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
client.get_document("does-not-exist")
|
|
102
|
+
except NotFoundError:
|
|
103
|
+
...
|
|
104
|
+
except AutosignlyError as error:
|
|
105
|
+
print(error.status_code, error.error_type, error.error_id)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Connection problems and server errors are retried automatically, with an exponential backoff and
|
|
109
|
+
jitter. Client errors are not retried, since repeating a rejected request cannot change its outcome.
|
|
110
|
+
|
|
111
|
+
Rate limits are retried too, honouring the delay the API asks for. When that delay is longer than a
|
|
112
|
+
minute the call fails instead of blocking your thread, and `RateLimitError.retry_after` tells you
|
|
113
|
+
how long to wait.
|
|
114
|
+
|
|
115
|
+
The client does not implement a circuit breaker. It runs inside your process, on calls you asked
|
|
116
|
+
for, so refusing to even attempt one would be surprising - and your own infrastructure is the right
|
|
117
|
+
place for that policy. Pass your own `http_client` if you want to add one.
|
|
118
|
+
|
|
119
|
+
## Links
|
|
120
|
+
|
|
121
|
+
- Website: <https://autosignly.eu>
|
|
122
|
+
- API documentation: <https://docs.16it.eu/docs/intro/>
|
|
123
|
+
- Source and issues: <https://github.com/16it-pl/autosignly-sdk>
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
Apache-2.0
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "autosignly"
|
|
3
|
+
description = "Python client for the Autosignly API - eIDAS electronic signatures and document workflows"
|
|
4
|
+
dynamic = ["version"]
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
license = "Apache-2.0"
|
|
8
|
+
authors = [{ name = "16it" }]
|
|
9
|
+
keywords = ["eidas", "electronic-signature", "esignature", "pdf", "pades", "autosignly"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 3 - Alpha",
|
|
12
|
+
"Intended Audience :: Developers",
|
|
13
|
+
"License :: OSI Approved :: Apache Software License",
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
16
|
+
]
|
|
17
|
+
dependencies = ["httpx>=0.27,<1"]
|
|
18
|
+
|
|
19
|
+
[project.urls]
|
|
20
|
+
Homepage = "https://autosignly.eu"
|
|
21
|
+
Documentation = "https://docs.16it.eu/docs/intro/"
|
|
22
|
+
Source = "https://github.com/16it-pl/autosignly-sdk"
|
|
23
|
+
Issues = "https://github.com/16it-pl/autosignly-sdk/issues"
|
|
24
|
+
|
|
25
|
+
[dependency-groups]
|
|
26
|
+
dev = ["pytest>=8"]
|
|
27
|
+
|
|
28
|
+
[build-system]
|
|
29
|
+
requires = ["hatchling"]
|
|
30
|
+
build-backend = "hatchling.build"
|
|
31
|
+
|
|
32
|
+
[tool.hatch.version]
|
|
33
|
+
path = "src/autosignly/_version.py"
|
|
34
|
+
|
|
35
|
+
[tool.hatch.build.targets.wheel]
|
|
36
|
+
packages = ["src/autosignly"]
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Python client for the Autosignly API.
|
|
2
|
+
|
|
3
|
+
from autosignly import AutosignlyClient, Signer
|
|
4
|
+
|
|
5
|
+
with AutosignlyClient(api_key="api_key_...", api_secret="api_sct_...") as client:
|
|
6
|
+
result = client.upload_and_sign(
|
|
7
|
+
pdf=open("contract.pdf", "rb").read(),
|
|
8
|
+
document_name="Contract",
|
|
9
|
+
signers=[Signer(first_name="Anna", last_name="Nowak",
|
|
10
|
+
email="anna@example.com", country="PL")],
|
|
11
|
+
)
|
|
12
|
+
print(result.document_id)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from ._version import __version__
|
|
16
|
+
from .client import AutosignlyClient, PRODUCTION_BASE_URL
|
|
17
|
+
from .errors import (
|
|
18
|
+
AutosignlyError,
|
|
19
|
+
AuthenticationError,
|
|
20
|
+
ConnectionError,
|
|
21
|
+
InvalidSignatureError,
|
|
22
|
+
NotFoundError,
|
|
23
|
+
PermissionDeniedError,
|
|
24
|
+
RateLimitError,
|
|
25
|
+
ServerError,
|
|
26
|
+
ValidationError,
|
|
27
|
+
)
|
|
28
|
+
from .models import (
|
|
29
|
+
Document,
|
|
30
|
+
DocumentStatus,
|
|
31
|
+
DocumentSummary,
|
|
32
|
+
Page,
|
|
33
|
+
SignatureType,
|
|
34
|
+
Signer,
|
|
35
|
+
SignerDetails,
|
|
36
|
+
SignerStatus,
|
|
37
|
+
SigningMode,
|
|
38
|
+
SigningRequestResult,
|
|
39
|
+
SigningStatus,
|
|
40
|
+
Tag,
|
|
41
|
+
VerificationMethod,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
__all__ = [
|
|
45
|
+
"AutosignlyClient",
|
|
46
|
+
"PRODUCTION_BASE_URL",
|
|
47
|
+
"AutosignlyError",
|
|
48
|
+
"AuthenticationError",
|
|
49
|
+
"ConnectionError",
|
|
50
|
+
"InvalidSignatureError",
|
|
51
|
+
"NotFoundError",
|
|
52
|
+
"PermissionDeniedError",
|
|
53
|
+
"RateLimitError",
|
|
54
|
+
"ServerError",
|
|
55
|
+
"ValidationError",
|
|
56
|
+
"Document",
|
|
57
|
+
"DocumentStatus",
|
|
58
|
+
"DocumentSummary",
|
|
59
|
+
"Page",
|
|
60
|
+
"SignatureType",
|
|
61
|
+
"Signer",
|
|
62
|
+
"SignerDetails",
|
|
63
|
+
"SignerStatus",
|
|
64
|
+
"SigningMode",
|
|
65
|
+
"SigningRequestResult",
|
|
66
|
+
"SigningStatus",
|
|
67
|
+
"Tag",
|
|
68
|
+
"VerificationMethod",
|
|
69
|
+
"webhooks",
|
|
70
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0.dev0"
|