Skip to main content

Assinafy Python SDK

Python SDK for the Assinafy API.

The SDK is synchronous, uses httpx, and covers every documented API group: authentication, documents, signers, signer documents, assignments, field definitions, templates, tags, and webhooks. Each public method's docstring names the exact HTTP verb and endpoint it calls, so you can cross-reference against the official docs in one step.

Requirements

  • Python 3.10+
  • httpx (installed automatically)

Installation

pip install assinafy

Quick Start

import os
from assinafy import AssinafyClient

client = AssinafyClient(
    api_key=os.environ["ASSINAFY_API_KEY"],
    account_id=os.environ["ASSINAFY_ACCOUNT_ID"],
    webhook_secret=os.environ.get("ASSINAFY_WEBHOOK_SECRET"),
)

result = client.upload_and_request_signatures(
    source={"file_path": "./contract.pdf"},
    signers=[
        {"full_name": "John Doe", "email": "john@example.com"},
        {"full_name": "Jane Smith", "email": "jane@example.com"},
    ],
    message="Please sign this contract",
)

print(result["document"]["id"])

Authentication

Prefer api_key; it is sent as the documented X-Api-Key header. token sends Authorization: Bearer <token> for legacy/user-token flows.

client = AssinafyClient(api_key="k_xxx", account_id="acc_xxx")
client = AssinafyClient(token="jwt_xxx", account_id="acc_xxx")

Unauthenticated clients are allowed for public and signer-access-code endpoints:

public_client = AssinafyClient()
session = public_client.authentication.login("user@example.com", "password")

Configuration

Parameter Type Default Description
api_key str None Sent as X-Api-Key.
token str None Sent as Authorization: Bearer <token>.
account_id str None Default workspace/account ID for account-scoped methods.
base_url str https://api.assinafy.com.br/v1 API base URL.
webhook_secret str None Secret used by WebhookVerifier.
timeout float 30.0 Request timeout in seconds.
logger object no-op Object with debug/info/warning/error methods.

Resources

Authentication

client.authentication.login("user@example.com", "password")
client.authentication.social_login("google", "provider-token", True)
client.authentication.create_api_key("password")
client.authentication.get_api_key()
client.authentication.delete_api_key()
client.authentication.change_password("user@example.com", "old", "new")
client.authentication.request_password_reset("user@example.com")
client.authentication.reset_password("user@example.com", "new", token="reset-token")

Documents

doc = client.documents.upload({"file_path": "./contract.pdf"})
doc = client.documents.upload({"buffer": pdf_bytes, "file_name": "contract.pdf"})

client.documents.statuses()
client.documents.list({"page": 1, "per_page": 20, "tags": "tag-id", "sort": "-updated_at"})
client.documents.search({"search": "nda", "status": "metadata_ready"})  # lightweight, compact
client.documents.get(doc["id"])
client.documents.rename(doc["id"], "Service agreement.pdf")  # before signing starts
client.documents.activities(doc["id"])
client.documents.wait_until_ready(doc["id"])
client.documents.download(doc["id"], "certificated")
client.documents.thumbnail(doc["id"])
client.documents.download_page(doc["id"], page_id)
client.documents.verify(signature_hash)
client.documents.public_info(doc["id"])
client.documents.send_token(doc["id"], "signer@example.com", "email")
client.documents.list_tags(doc["id"])
client.documents.replace_tags(doc["id"], ["Contracts", "2026-Q1"])
client.documents.append_tags(doc["id"], ["Urgent"])
client.documents.detach_tag(doc["id"], tag_id)
client.documents.delete(doc["id"])

Uploads follow the documented multipart shape and are locally limited to PDF files up to 25 MB.

Templates

templates = client.templates.list({"search": "NDA", "tags": "tag-id", "per_page": 20})
template = client.templates.get(template_id)

client.documents.create_from_template(
    template_id,
    [{"role_id": "role-id", "id": signer_id, "verification_method": "Email"}],
    {"name": "NDA - John Doe", "message": "Please sign."},
)

client.documents.estimate_cost_from_template(
    template_id,
    [{"role_id": "role-id", "id": signer_id}],
)

Tags

tags = client.tags.list({"search": "contract", "per_page": 20})
tag = client.tags.create({"name": "Contracts", "color": "ff8800"})
client.tags.update(tag["id"], {"name": "Sales Contracts"})
client.tags.update(tag["id"], {"color": None})  # clears color
client.tags.delete(tag["id"])
client.tags.delete(tag["id"], force=True)

Signers

signer = client.signers.create({
    "full_name": "John Doe",
    "email": "john@example.com",
})

client.signers.create({
    "full_name": "Jane Doe",
    "whatsapp_phone_number": "+5548999990000",
})

client.signers.get(signer["id"])
client.signers.list({"search": "john", "per_page": 50})
client.signers.update(signer["id"], {"full_name": "Johnny Doe"})
client.signers.delete(signer["id"])
client.signers.find_by_email("john@example.com")

Signer-access-code endpoints:

client.signers.get_self(signer_access_code)
client.signers.accept_terms(signer_access_code)
client.signers.verify_email(signer_access_code, "123456")
client.signers.confirm_data(
    document_id,
    signer_access_code,
    {"email": "john@example.com", "has_accepted_terms": True},
)
client.signers.upload_signature(signer_access_code, png_bytes, "signature")
client.signers.download_signature(signer_access_code, "signature")

Assignments

client.assignments.list({"page": 1, "per_page": 20})  # assignments for the account
client.assignments.estimate_cost(document_id, {"signers": [{"verification_method": "Email"}]})

assignment = client.assignments.create(document_id, {
    "method": "virtual",
    "signers": [
        # `step` controls sequential signing order (signers sharing a step sign
        # in parallel; the next step is notified once the previous one finishes).
        {"id": signer_a["id"], "verification_method": "Email", "step": 1},
        {"id": signer_b["id"], "verification_method": "Email", "step": 2},
    ],
    "message": "Please review and sign",
    "expires_at": "2026-12-31T00:00:00Z",
})

client.assignments.reset_expiration(document_id, assignment["id"], "2027-01-31T00:00:00Z")
client.assignments.reset_expiration(document_id, assignment["id"], None)  # clears expiration
client.assignments.resend_notification(document_id, assignment["id"], signer["id"])
client.assignments.estimate_resend_cost(document_id, assignment["id"], signer["id"])
client.assignments.whatsapp_notifications(document_id, assignment["id"])

Signer-facing assignment endpoints:

client.assignments.get_for_signer(signer_access_code)
client.assignments.sign(document_id, assignment_id, [{"itemId": "item-1"}], signer_access_code)
client.assignments.decline(document_id, assignment_id, "I do not agree.", signer_access_code)

Signer Documents

client.signer_documents.current(signer_id, signer_access_code)
client.signer_documents.list(signer_id, signer_access_code, {"status": "pending_signature"})
client.signer_documents.sign_multiple(["doc-1", "doc-2"], signer_access_code)
client.signer_documents.decline_multiple(["doc-1"], "Unfavorable terms.", signer_access_code)
client.signer_documents.download(signer_id, document_id, signer_access_code, "original")

Field Definitions

field = client.fields.create({"type": "text", "name": "CPF"})
client.fields.list({"include_standard": True})
client.fields.get(field["id"])
client.fields.update(field["id"], {"name": "CPF updated"})
client.fields.validate(field["id"], "400.676.228-36", signer_access_code=signer_access_code)
client.fields.validate_multiple(
    [{"field_id": field["id"], "value": "400.676.228-36"}],
    signer_access_code=signer_access_code,
)
client.fields.list_types()
client.fields.delete(field["id"])

Webhooks

client.webhooks.register({
    "url": "https://example.com/webhooks/assinafy",
    "email": "admin@example.com",
    "events": ["document_ready", "signer_signed_document"],
})

client.webhooks.get()
client.webhooks.inactivate()  # stops delivery; the API has no DELETE endpoint
client.webhooks.list_event_types()
client.webhooks.list_dispatches({"delivered": False, "page": 1, "per_page": 20})
client.webhooks.retry_dispatch(dispatch_id)

A workspace has a single webhook subscription. There is no documented DELETE endpoint — call inactivate() to stop delivery (it preserves the configured URL/events) and register() again to re-enable.

Webhooks: Parsing Payloads

Every webhook body shares the documented envelope: id, event, message, payload (event-specific params), origin, created_at, subject (the entity that acted), object (the entity acted on), and account_id.

raw_body = request.get_data()

event = client.webhook_verifier.extract_event(raw_body)
event_type = client.webhook_verifier.get_event_type(event)      # e.g. "document_ready"
params = client.webhook_verifier.get_event_payload(event)       # event-specific params
subject = client.webhook_verifier.get_event_subject(event)      # actor (+ "type")
target = client.webhook_verifier.get_event_object(event)        # target (+ "type")
# get_event_data(event) is a backward-compatible alias of get_event_object(event)

Signature verification

The documented Delivery Contract specifies the HTTP method, Content-Type, retry, and circuit-breaker behavior, but does not define any signature header or shared-secret scheme. verify() is provided only for accounts that have separately arranged an HMAC-SHA256 scheme with Assinafy:

signature = request.headers.get("X-Assinafy-Signature", "")
if not client.webhook_verifier.verify(raw_body, signature):
    return "Invalid signature", 401

Query Parameters

The SDK accepts Pythonic aliases for documented hyphenated query parameters. For example, per_page is sent as per-page, and signer_access_code is sent as signer-access-code.

Errors

The SDK raises typed errors; every failure raises a subclass of AssinafyError.

from assinafy import ApiError, AssinafyError, NetworkError, ValidationError

try:
    client.documents.upload({"file_path": "./contract.pdf"})
except ValidationError as err:
    print("Validation failed:", err.errors)
except ApiError as err:
    print(f"API error {err.status_code}:", err.response_data)
except NetworkError as err:
    print("Network error:", err)
except AssinafyError as err:
    print("SDK error:", err, err.context)

Development

pip install -e ".[dev]"
pytest
mypy src
ruff check src tests

Live smoke test

ASSINAFY_API_KEY=... ASSINAFY_ACCOUNT_ID=... python scripts/live_smoke.py

Hits the live API to confirm read endpoints, signer CRUD, document upload, document tagging, tag CRUD, wait_until_ready polling, cost estimation, and cleanup all work end-to-end.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

assinafy-1.4.0.tar.gz (45.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

assinafy-1.4.0-py3-none-any.whl (39.8 kB view details)

Uploaded Python 3

File details

Details for the file assinafy-1.4.0.tar.gz.

File metadata

  • Download URL: assinafy-1.4.0.tar.gz
  • Upload date:
  • Size: 45.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for assinafy-1.4.0.tar.gz
Algorithm Hash digest
SHA256 b7bc40bfdcbdd72fc3a3d6619ca730400a7250b6abed6e810abf2d5c794eca2b
MD5 22394aa0ae3f69244fef512e20e683b2
BLAKE2b-256 a5b36988f83891c50fae7ba9d35c87e6b149a891e3b59769ff7f8e7b12be1642

See more details on using hashes here.

Provenance

The following attestation bundles were made for assinafy-1.4.0.tar.gz:

Publisher: release.yml on assinafy/python-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file assinafy-1.4.0-py3-none-any.whl.

File metadata

  • Download URL: assinafy-1.4.0-py3-none-any.whl
  • Upload date:
  • Size: 39.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for assinafy-1.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 52803cd95618bcaa58a474b243728fa53a9f5a6607f00d975b024bab8e1e2d20
MD5 eeccd0370a017d372d6aacea5785c2d0
BLAKE2b-256 52c6c2775004f7e9551d20cbcc52a15cc80df3238b307470cafd4c2009c8295b

See more details on using hashes here.

Provenance

The following attestation bundles were made for assinafy-1.4.0-py3-none-any.whl:

Publisher: release.yml on assinafy/python-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

1.6.2

2 files

1.6.1

2 files

1.6.0

2 files

1.5.0

2 files

This release

1.4.0 This release

2 files

1.3.1

2 files

1.3.0

2 files

1.1.1

2 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