Official Python SDK for the Formable API (v1). Covers templates, signature requests, redlining, and billing.
- Sync (
Formable) and async (AsyncFormable) clients - Fully typed requests and responses (
py.typed) - Python 3.9+
pip install formable-sdkimport os
from formable import Formable
formable = Formable(api_key=os.environ["FORMABLE_API_KEY"])with open("nda.docx", "rb") as f:
result = formable.templates.create(
file=f.read(),
filename="nda.docx",
signer_roles=[
{"name": "Client", "order": 0},
{"name": "Witness", "order": 1},
],
)
template_id = result["templateId"]
# Mint a fresh edit URL later (expires after 1 day)
edit = formable.templates.create_edit_url(template_id)
print(edit["editUrl"], edit["expiresAt"])# Formable emails each signer a signing link
request = formable.signature_requests.create(
template_id=template_id,
signers=[
{"email": "jane@example.com", "name": "Jane Doe", "role": "Client"},
{"email": "bob@example.com", "name": "Bob Smith", "role": "Witness"},
],
)
# Embedded flow: mint signing URLs to embed in an iframe yourself
embedded = formable.signature_requests.create_embedded(
template_id=template_id,
signers=[{"email": "jane@example.com", "name": "Jane Doe", "role": "Client"}],
test_mode=True,
)
signer = embedded["signers"][0]
signing = formable.signature_requests.create_signing_url(
signer["recipientSignatureId"]
)
# Track progress
from datetime import datetime, timezone
current = formable.signature_requests.get(embedded["signatureRequestId"])
all_requests = formable.signature_requests.list(
updated_since=datetime(2026, 1, 1, tzinfo=timezone.utc)
)
events = formable.signature_requests.get_events(embedded["signatureRequestId"])
# Download the signed document once completed
envelope = formable.signature_requests.get_signed_envelope(
embedded["signatureRequestId"]
)
print(envelope["signedEnvelopePresignedUrl"])created = formable.redline_requests.create(
template_id=template_id,
members=[
{"email": "us@example.com", "display_name": "John Doe", "role": "DisclosingParty"},
{"email": "them@example.com", "display_name": "Jane Smith", "role": "ReceivingParty"},
],
metadata={"subject": "Mutual NDA"},
)
redline_request_id = created["redlineRequestId"]
# Mint a redline URL for a member (embed in an iframe)
url = formable.redline_requests.create_url(redline_request_id, "them@example.com")
# Manage members and track progress
formable.redline_requests.update_members(
redline_request_id,
[{"email": "counsel@example.com", "display_name": "Counsel", "role": "ReceivingCounsel"}],
)
redline = formable.redline_requests.get(redline_request_id)
events = formable.redline_requests.get_events(redline_request_id)billing = formable.billing()
print(billing["numberOfRedliningSessions"])
health = formable.health()Every method is also available on AsyncFormable with the same signatures.
import asyncio
from formable import AsyncFormable
async def main():
async with AsyncFormable(api_key=os.environ["FORMABLE_API_KEY"]) as formable:
health = await formable.health()
asyncio.run(main())All non-2xx responses raise a FormableError with the server's error message, HTTP status, and parsed response body.
from formable import FormableError
try:
formable.signature_requests.get("missing-id")
except FormableError as error:
print(error.status, error)| Option | Description | Default |
|---|---|---|
api_key |
Your Formable API key (sent as a bearer token). Required. | - |
base_url |
Override the API base URL. | https://api.formabledocs.com/v1 |
client |
Custom httpx.Client (or httpx.AsyncClient for async). |
Built-in client with 60s timeout |
python3 -m venv .venv
.venv/bin/pip install -e . pytest mypy build
.venv/bin/python -m pytest tests
.venv/bin/python -m mypy src/formable.venv/bin/python -m build
.venv/bin/python -m pip install twine
.venv/bin/python -m twine upload dist/*