Skip to main content

autosignly

Python client for the Autosignly API - eIDAS electronic signatures and document workflows.

Install

pip install autosignly

Requires Python 3.10 or newer.

Stable release. The supported line is 1.0.*, ready for production use; the newest version is listed under tags.

Quickstart

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

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)

Each signer carries signed_at, set when that person signed and None while their signature is outstanding. It is the only per-signer progress the API reports: document.status says whether everyone is done, not who.

Both listing calls take tag_id as well. Several tags narrow the result — a document has to carry all of them — and a tag that does not exist gives an empty page rather than an error:

page = client.list_documents(tag_id=["contracts", "2026"], status="SIGNED")

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.

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.

What a signer may be asked for

The rules differ by country, and a signer sent with a combination their country does not allow is rejected when the document goes out. Read them first:

policy = client.get_signature_policy(signer.country)
for allowed in policy.signature_types:
    print(allowed.type, allowed.verification_methods)

A country without its own rules answers with the fallback policy rather than an error.

Verifying by SMS also needs a reachable phone number:

prefixes = {c.country_code: c.dialing_prefix for c in client.list_sms_countries()}

A number outside that list is refused when the code is requested — which happens after the document has already gone out, so check it while preparing the signer.

Attachments

Files attached to a document are converted to PDF and merged into it when it is sent for signing, behind an index page listing each one with its checksum — so a single signature covers the document and everything attached to it.

Attachments can only be added before the document is sent, so upload it first and send it afterwards instead of using upload_and_sign:

document_id = client.upload_pdf(
    pdf=open("protocol.pdf", "rb").read(),
    document_name="Handover protocol",
)

attachment = client.add_attachment(
    document_id,
    content=open("site-photo.jpg", "rb").read(),
    file_name="site-photo.jpg",
)
print(attachment.order_index, attachment.sha256)

for existing in client.list_attachments(document_id):
    print(existing.file_name, existing.page_count)

client.send_for_signing(document_id, signers=[signer])

An attachment can be dropped again while the document is still unsent:

client.delete_attachment(document_id, attachment.id)

PDF, JPEG and PNG are accepted, recognised from the content rather than the file name. Attachments merge in the order they were added, and can only be changed before the document is sent for signing.

Tags

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.

Parties

A party is the other side of a document — a business or a natural person the company signs with.

from autosignly import Party, PartyAddress, PartyType

acme = client.create_party(Party(
    type=PartyType.COMPANY,
    name="Acme Sp. z o.o.",
    tax_id="5842831253",
    email="kontakt@acme.pl",
    address=PartyAddress(street="Marszalkowska", number="12/34",
                         postal_code="00-001", city="Warszawa", country_code="PL"),
))

for party in client.list_parties(name="acme", type=PartyType.COMPANY):
    print(party.id, party.name, party.tax_id)

client.update_party(acme.id, Party(type=PartyType.COMPANY, name="Acme Renamed",
                                   tax_id="5842831253"))
client.delete_party(acme.id)

A COMPANY needs a tax_id and an address; a PERSON needs a firstname and an email. A Polish address makes the tax id subject to the NIP checksum.

update_party replaces the whole party, so send every field you want to keep. Creating a party that already exists — same tax id for a COMPANY, same e-mail for a PERSON — is rejected rather than deduplicated, so look the party up before retrying a failed create.

Parties belong to the environment of the key that created them: a sandbox key never sees a production party. Listing has no sort — the searchable fields are stored encrypted, so the server cannot order by them.

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.

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.

What a delivery carries

Two event types, both naming the document in documentId:

eventType when also carries
DOCUMENT_SIGNED one signer has signed signerId, and email of the person who signed
DOCUMENT_ALL_SIGNATURES_DONE the document is finished, closing seal included nothing else

DOCUMENT_ALL_SIGNATURES_DONE arrives after finalization, not when the last signature lands, so the file behind fileUrl is the final one by the time you act on it.

Errors

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

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.

Writes carry an Idempotency-Key header, generated once per call and kept across the retries of that call, and the API honours it: a retry whose original has already finished gets the stored response back instead of creating a second document, and a retry that catches the original still in flight is rejected with 409 (a ValidationError) rather than duplicating the write. Successful responses are remembered for 24 hours. Retrying a write yourself is safe under the same rule — send the same key with the same body.

Links

License

Apache-2.0

Release files for autosignly 1.0.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for autosignly 1.0.2
File Size Uploaded
autosignly-1.0.2.tar.gz 22.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for autosignly 1.0.2
File Interpreter ABI Platform
autosignly-1.0.2-py3-none-any.whl Python 3 none any Details

Total release size: 40.7 kB

Release files / autosignly-1.0.2.tar.gz

Download URL autosignly-1.0.2.tar.gz
Size 22.4 kB
Tags Source
SHA-256 checksum
How to use checksums
98c9adc7492610bb403c3bf8f3bea89ea98583b43bb3fafd0145dd938a40585d
BLAKE2b-256 checksum
How to use checksums
67305173af9432ed1a5211d56a650a2b7ef79c7463a40d4ef8cc727567597b8c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.

Transparency log

Release files / autosignly-1.0.2-py3-none-any.whl

Download URL autosignly-1.0.2-py3-none-any.whl
Size 18.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
99567beaf32b89c0bb13839a46a6307009ca1ab8604874bcfd14d285d7ded5c3
BLAKE2b-256 checksum
How to use checksums
090e7c7659179b763f283f8ad95da1be36db47b286f21c23d4abaa79c7fa253f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.

Transparency log

Release history Release notifications | RSS feed

1.0.3

2 release files

This release

1.0.2 This release

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page