Docs

Docs / Start / Python client

Python client

signsealer for Python 3.9 and later. The standard library only: no requests, nothing to pin.

pip install signsealer

On its way to PyPI. The client is written and tested against this API; until its first release is published, the install line above does not find it. Ask us and we will send the package.

It makes the same three promises as the Node client: a write that creates something carries an idempotency key, made for you when you do not pass one, so a retry after a lost answer never makes a second document; the failures worth retrying are retried, honouring a Retry-After the server sends; and an error carries the API's own sentence, its code, its hint and the request id.

Send a document

import os
from signsealer import SignSealer, SignSealerError

signsealer = SignSealer(api_key=os.environ["SIGNSEALER_KEY"])

doc = signsealer.prepare_document({
    "template_code": "rental-agreement",
    "values": {"guest_name": "Dana Reyes", "trip_date": "2026-11-14"},
    "external_ref": "booking-1042",
})
sent = signsealer.send(doc["document_id"], [{"full_name": "Dana Reyes", "email": "dana@example.com"}])
print(sent["links"][0]["url"])

Every method

MethodCalls
account() · branding()GET /v1/account · /v1/branding
templates(code=None) · template(id)GET /v1/templates · /v1/templates/:id
documents(status=, q=, external_ref=, limit=, offset=) · document(id) · document_signers(id)GET /v1/documents · /:id · /:id/signers
prepare_document(document, idempotency_key=None)POST /v1/documents
document_from_pdf(title, pdf, fields=None, text_tags=False)POST /v1/documents/from-pdf: pdf is bytes or base64
send(document_id, signers) · remind(document_id, signer_id) · embed_link(document_id, signer_id) · void(document_id, reason)POST /v1/documents/:id/send · /signers/:signer/remind · /embed · /void
certificate(id) · pdf(id) · bundle(id)GET /v1/documents/:id/certificate · /pdf · /bundle: the files come back as bytes
standing(template, email=None, phone=None)GET /v1/standing
workflows() · start_workflow(workflow_id, participant)GET /v1/workflows · POST /v1/workflows/:id/start
webhooks() · add_webhook(url, events=None) · ensure_webhook(url, events=None) · remove_webhook(id, reason)/v1/webhooks

Each answers with the API's JSON as a dict, exactly as the API reference describes it. What the Node client has and this one does not yet (templates you write from code, packets, tablets, the signer's side) is reachable through the same API.

Errors

try:
    signsealer.prepare_document({"template_code": "rental-agreement", "values": {}})
except SignSealerError as e:
    print(e.status, e.code, e.message)   # 422 invalid no value for guest_name
    print(e.hint, e.request_id)          # what to do, and the id to quote

Verifying a webhook

from signsealer import verify_webhook

check = verify_webhook(
    secret=os.environ["SIGNSEALER_WEBHOOK_SECRET"],
    body=request.get_data(),                       # the bytes that arrived
    signature=request.headers["signsealer-signature"],
    timestamp=request.headers["signsealer-timestamp"],
)
if not check.ok:
    return check.reason, 400

The timestamp is inside the signature and the five-minute window holds both ways; during a secret rotation either secret verifies; every part is compared in constant time. Webhooks has the events.

Ready to build? An API key takes a minute in the portal, and the free plan covers the first 10 agreements a month.

Get an API key