autosignly 0.1.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,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,158 @@
1
+ Metadata-Version: 2.5
2
+ Name: autosignly
3
+ Version: 0.1.0
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 delivery. Check the signature against the raw request body, before parsing
101
+ 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(
107
+ request.body,
108
+ request.headers["X-Webhook-Signature"],
109
+ webhook_key,
110
+ request.headers["X-Webhook-Timestamp"],
111
+ )
112
+ ```
113
+
114
+ The signature covers the timestamp as well as the body, and a delivery older than five minutes is
115
+ rejected even when its signature matches, so a captured request cannot be replayed later.
116
+
117
+ While a webhook key is being rotated a delivery carries several signatures; it is accepted when any
118
+ of them matches, so rotation needs no change on your side.
119
+
120
+ `verify` raises `InvalidSignatureError` on a mismatch; `webhooks.is_valid(...)` returns a boolean
121
+ instead.
122
+
123
+ ## Errors
124
+
125
+ Every failure raises a subclass of `AutosignlyError` carrying the HTTP status and the error type
126
+ returned by the API.
127
+
128
+ ```python
129
+ from autosignly import AutosignlyError, NotFoundError
130
+
131
+ try:
132
+ client.get_document("does-not-exist")
133
+ except NotFoundError:
134
+ ...
135
+ except AutosignlyError as error:
136
+ print(error.status_code, error.error_type, error.error_id)
137
+ ```
138
+
139
+ Connection problems and server errors are retried automatically, with an exponential backoff and
140
+ jitter. Client errors are not retried, since repeating a rejected request cannot change its outcome.
141
+
142
+ Rate limits are retried too, honouring the delay the API asks for. When that delay is longer than a
143
+ minute the call fails instead of blocking your thread, and `RateLimitError.retry_after` tells you
144
+ how long to wait.
145
+
146
+ The client does not implement a circuit breaker. It runs inside your process, on calls you asked
147
+ for, so refusing to even attempt one would be surprising - and your own infrastructure is the right
148
+ place for that policy. Pass your own `http_client` if you want to add one.
149
+
150
+ ## Links
151
+
152
+ - Website: <https://autosignly.eu>
153
+ - API documentation: <https://docs.16it.eu/docs/intro/>
154
+ - Source and issues: <https://github.com/16it-pl/autosignly-sdk>
155
+
156
+ ## License
157
+
158
+ Apache-2.0
@@ -0,0 +1,138 @@
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 delivery. Check the signature against the raw request body, before parsing
81
+ 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(
87
+ request.body,
88
+ request.headers["X-Webhook-Signature"],
89
+ webhook_key,
90
+ request.headers["X-Webhook-Timestamp"],
91
+ )
92
+ ```
93
+
94
+ The signature covers the timestamp as well as the body, and a delivery older than five minutes is
95
+ rejected even when its signature matches, so a captured request cannot be replayed later.
96
+
97
+ While a webhook key is being rotated a delivery carries several signatures; it is accepted when any
98
+ of them matches, so rotation needs no change on your side.
99
+
100
+ `verify` raises `InvalidSignatureError` on a mismatch; `webhooks.is_valid(...)` returns a boolean
101
+ instead.
102
+
103
+ ## Errors
104
+
105
+ Every failure raises a subclass of `AutosignlyError` carrying the HTTP status and the error type
106
+ returned by the API.
107
+
108
+ ```python
109
+ from autosignly import AutosignlyError, NotFoundError
110
+
111
+ try:
112
+ client.get_document("does-not-exist")
113
+ except NotFoundError:
114
+ ...
115
+ except AutosignlyError as error:
116
+ print(error.status_code, error.error_type, error.error_id)
117
+ ```
118
+
119
+ Connection problems and server errors are retried automatically, with an exponential backoff and
120
+ jitter. Client errors are not retried, since repeating a rejected request cannot change its outcome.
121
+
122
+ Rate limits are retried too, honouring the delay the API asks for. When that delay is longer than a
123
+ minute the call fails instead of blocking your thread, and `RateLimitError.retry_after` tells you
124
+ how long to wait.
125
+
126
+ The client does not implement a circuit breaker. It runs inside your process, on calls you asked
127
+ for, so refusing to even attempt one would be surprising - and your own infrastructure is the right
128
+ place for that policy. Pass your own `http_client` if you want to add one.
129
+
130
+ ## Links
131
+
132
+ - Website: <https://autosignly.eu>
133
+ - API documentation: <https://docs.16it.eu/docs/intro/>
134
+ - Source and issues: <https://github.com/16it-pl/autosignly-sdk>
135
+
136
+ ## License
137
+
138
+ 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,76 @@
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
+ Credentials,
30
+ Document,
31
+ DocumentStatus,
32
+ EnvironmentType,
33
+ DocumentSummary,
34
+ Page,
35
+ SignatureMode,
36
+ SignatureType,
37
+ Signer,
38
+ SignerDetails,
39
+ SignerStatus,
40
+ SigningMode,
41
+ SigningRequestResult,
42
+ SigningStatus,
43
+ Tag,
44
+ VerificationMethod,
45
+ )
46
+
47
+ __all__ = [
48
+ "AutosignlyClient",
49
+ "PRODUCTION_BASE_URL",
50
+ "AutosignlyError",
51
+ "AuthenticationError",
52
+ "ConnectionError",
53
+ "InvalidSignatureError",
54
+ "NotFoundError",
55
+ "PermissionDeniedError",
56
+ "RateLimitError",
57
+ "ServerError",
58
+ "ValidationError",
59
+ "Credentials",
60
+ "Document",
61
+ "DocumentStatus",
62
+ "EnvironmentType",
63
+ "DocumentSummary",
64
+ "Page",
65
+ "SignatureMode",
66
+ "SignatureType",
67
+ "Signer",
68
+ "SignerDetails",
69
+ "SignerStatus",
70
+ "SigningMode",
71
+ "SigningRequestResult",
72
+ "SigningStatus",
73
+ "Tag",
74
+ "VerificationMethod",
75
+ "webhooks",
76
+ ]
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"