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"])

upload_and_request_signatures chains three calls (upload, create each signer, create the assignment) and is not transactional — a failure partway through does not roll back what already succeeded. It also accepts wait_timeout / wait_poll_interval to override the default document-readiness poll.

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},
    # also accepts "full_name" and "government_id"
)
client.signers.upload_signature(signer_access_code, png_bytes, "signature")
client.signers.upload_signature(signer_access_code, png_bytes, reuse=True)  # sets is_signature_reusable
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.search(signer_id, signer_access_code, "contract")  # lightweight, compact
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 --cov=assinafy --cov-report=term-missing
mypy src
ruff check src tests
ruff format --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/tag/field CRUD (including clearing a field's regex), template lookup and cost estimation, document upload, document tagging, wait_until_ready polling, cost estimation, and cleanup all work end-to-end. It saves and restores the workspace's webhook subscription around its own register/inactivate test, since a workspace only has one.

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.5.0.tar.gz (52.9 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.5.0-py3-none-any.whl (41.2 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for assinafy-1.5.0.tar.gz
Algorithm Hash digest
SHA256 35ba6d9d1e4512ece6830cbd550de806004e2aaea730b02c7eaebb8a59d7df25
MD5 e8196a5a21c73428026308dbd8dd2b50
BLAKE2b-256 87bef9bb53f55e8304fd9c01e7874915b1939108186845320307fef840e92374

See more details on using hashes here.

Provenance

The following attestation bundles were made for assinafy-1.5.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.5.0-py3-none-any.whl.

File metadata

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

File hashes

Hashes for assinafy-1.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 09163987c425214cb5264c4f3287a5250f1be8396a78fcf21def884701409028
MD5 6df2630569027621bf64fba56a5aab20
BLAKE2b-256 976881aab3b0099fa9f598290be515ed1200e26a3ed986bc06cb22c327072e56

See more details on using hashes here.

Provenance

The following attestation bundles were made for assinafy-1.5.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

This release

1.5.0 This release

2 files

1.4.0

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