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
| Method | Calls |
|---|---|
| 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= | POST /v1/documents |
| document_from_pdf(title, pdf, fields=None, text_tags=False) | POST /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 / |
| certificate(id) · pdf(id) · bundle(id) | GET /bytes |
| standing(template, email=None, phone=None) | GET /v1/standing |
| workflows() · start_workflow(workflow_id, participant) | GET /v1/workflows · POST / |
| 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