Loading...
Loading...
The SignBolt Python SDK provides a clean, idiomatic Python client for the SignBolt REST API. Compatible with Python 3.9+ and works in Flask, FastAPI, Django, and any async or sync Python codebase.
Type hints throughout (compatible with mypy and pyright)
Sync and async clients (SignBolt and AsyncSignBolt)
Pydantic models for all request and response types
Automatic retry with exponential backoff on transient errors
Webhook signature verification helper
Python 3.9 or newer required. The SDK uses httpx internally for HTTP, which is installed automatically. For older Python (3.7, 3.8), pin to the legacy SDK release.
pip install signboltpoetry add signbolt # or uv add signboltGenerate an API key from your SignBolt dashboard at /dashboard. API keys are prefixed `sb_live_` (production) or `sb_test_` (sandbox). Load from environment variables β never commit keys to source.
from signbolt import SignBolt
import os
# Load from environment
client = SignBolt(api_key=os.environ['SIGNBOLT_API_KEY'])
# With additional options
client = SignBolt(
api_key=os.environ['SIGNBOLT_API_KEY'],
base_url='https://signbolt.au/api/v1', # default
timeout=30.0, # seconds
max_retries=3,
)
# Async client
from signbolt import AsyncSignBolt
async_client = AsyncSignBolt(api_key=os.environ['SIGNBOLT_API_KEY'])Send a document to a recipient for signature.
from signbolt import SignBolt
from signbolt.models import SignatureField, Recipient
import os
client = SignBolt(api_key=os.environ['SIGNBOLT_API_KEY'])
def send_contract():
# Read PDF from disk
with open('contracts/service-agreement.pdf', 'rb') as f:
pdf_bytes = f.read()
response = client.documents.send(
file=pdf_bytes,
filename='service-agreement.pdf',
recipients=[
Recipient(
email='client@example.com',
name='Jane Client',
role='signer',
),
],
subject='Please sign: Service Agreement',
message='Hi Jane, please review and sign the attached service agreement.',
fields=[
SignatureField(
type='signature',
page=1,
x=100,
y=500,
width=200,
height=60,
required=True,
recipient_email='client@example.com',
),
SignatureField(
type='date',
page=1,
x=350,
y=510,
width=120,
height=40,
required=True,
recipient_email='client@example.com',
),
],
webhook_url='https://api.yoursite.com/webhooks/signbolt',
expires_in_days=14,
)
print(f'Document ID: {response.document_id}')
print(f'Signing URL: {response.signing_urls[0].url}')
print(f'Status: {response.status}')
return response
# Async equivalent
from signbolt import AsyncSignBolt
import asyncio
async def send_contract_async():
async_client = AsyncSignBolt(api_key=os.environ['SIGNBOLT_API_KEY'])
with open('contracts/service-agreement.pdf', 'rb') as f:
pdf_bytes = f.read()
response = await async_client.documents.send(
file=pdf_bytes,
filename='service-agreement.pdf',
recipients=[Recipient(email='client@example.com', name='Jane Client')],
fields=[SignatureField(type='signature', page=1, x=100, y=500, width=200, height=60)],
)
return response
asyncio.run(send_contract_async())Verify incoming webhook signatures using Flask or FastAPI.
# Flask example
from flask import Flask, request, jsonify
from signbolt import SignBolt
import os
import json
app = Flask(__name__)
client = SignBolt(api_key=os.environ['SIGNBOLT_API_KEY'])
webhook_secret = os.environ['SIGNBOLT_WEBHOOK_SECRET']
@app.route('/webhooks/signbolt', methods=['POST'])
def signbolt_webhook():
signature = request.headers.get('X-SignBolt-Signature')
body = request.get_data()
# Verify signature
if not client.webhooks.verify(body=body, signature=signature, secret=webhook_secret):
return jsonify({'error': 'Invalid signature'}), 401
event = json.loads(body)
event_type = event['type']
if event_type == 'document.signed':
document_id = event['data']['documentId']
signer = event['data']['signerEmail']
print(f'Document {document_id} signed by {signer}')
# Update database, send notification
elif event_type == 'document.completed':
document_id = event['data']['documentId']
print(f'Document {document_id} fully signed')
# Download final PDF
elif event_type == 'document.declined':
print(f'Signer declined: {event["data"]["signerEmail"]}')
elif event_type == 'document.expired':
print(f'Document {event["data"]["documentId"]} expired')
return jsonify({'received': True}), 200
# FastAPI example
from fastapi import FastAPI, Request, HTTPException
from signbolt import AsyncSignBolt
app = FastAPI()
async_client = AsyncSignBolt(api_key=os.environ['SIGNBOLT_API_KEY'])
@app.post('/webhooks/signbolt')
async def signbolt_webhook(request: Request):
signature = request.headers.get('x-signbolt-signature')
body = await request.body()
if not async_client.webhooks.verify(body=body, signature=signature, secret=webhook_secret):
raise HTTPException(status_code=401, detail='Invalid signature')
event = json.loads(body)
if event['type'] == 'document.completed':
# Fetch the signed PDF
signed_doc = await async_client.documents.get(event['data']['documentId'])
# ... process the signed document
return {'received': True}Handle errors with the typed SignBoltError exception class.
from signbolt import SignBolt, SignBoltError
import os
client = SignBolt(api_key=os.environ['SIGNBOLT_API_KEY'])
def send_with_error_handling(pdf_bytes: bytes):
try:
response = client.documents.send(
file=pdf_bytes,
filename='contract.pdf',
recipients=[{'email': 'client@example.com', 'name': 'Client'}],
fields=[{'type': 'signature', 'page': 1, 'x': 100, 'y': 500, 'width': 200, 'height': 60}],
)
return response
except SignBoltError as e:
if e.status_code == 400:
print(f'Bad request: {e.message}')
# e.details has field-level validation errors
elif e.status_code == 401:
print('Invalid API key β check credentials')
elif e.status_code == 403:
print('API access requires Business plan β upgrade at /pricing')
elif e.status_code == 413:
print('PDF exceeds 25MB β compress or split')
elif e.status_code == 429:
print('Rate limited β SDK will retry automatically')
elif e.status_code >= 500:
print('SignBolt server error β transient, safe to retry')
else:
print(f'SignBolt error: {e.message}')
raise
except Exception as e:
# Non-SignBolt error (network, parsing)
print(f'Unexpected error: {e}')
raiseList signed documents with pagination and filtering.
from signbolt import SignBolt
from datetime import datetime, timezone
import os
client = SignBolt(api_key=os.environ['SIGNBOLT_API_KEY'])
# Recent completed documents
def get_recent_signed():
page1 = client.documents.list(
status='completed',
limit=50,
)
print(f'Got {len(page1.data)} documents')
# Paginate
if page1.next_cursor:
page2 = client.documents.list(
status='completed',
limit=50,
cursor=page1.next_cursor,
)
print(f'Got another {len(page2.data)} documents')
# Filter by date range
def get_docs_for_march():
docs = client.documents.list(
created_after=datetime(2026, 3, 1, tzinfo=timezone.utc),
created_before=datetime(2026, 4, 1, tzinfo=timezone.utc),
limit=100,
)
for doc in docs.data:
print(f'{doc.filename} β signed by {doc.signer_email} on {doc.signed_at}')
# Iterate all documents (handles pagination automatically)
def iterate_all():
for doc in client.documents.list_all(status='completed'):
print(doc.filename)The SignBolt API enforces rate limits to ensure fair use across all customers. The SDK respects these limits with automatic retries.
Standard rate limit: 60 requests per minute per API key.
Burst allowance: up to 120 requests in a 60-second window.
Hourly limit: 3,000 requests per API key per hour.
Send endpoint: limited to 10 documents per minute to protect email deliverability.
SDK retries rate-limited requests automatically with exponential backoff (max_retries, default 3).
Yes. Import `AsyncSignBolt` instead of `SignBolt` for an asyncio-compatible client. All methods on `AsyncSignBolt` are coroutines. Both clients share the same API surface β the only difference is sync vs async.
Yes. Use the sync `SignBolt` client in Django views or management commands. For Django-async views or ASGI, use `AsyncSignBolt`. The SDK has no Django-specific dependencies β drop it into any Django project.
Yes, and FastAPI is the natural fit for the `AsyncSignBolt` client. Use dependency injection to provide the SignBolt client to your route handlers. Webhook endpoints should use FastAPI's `Request.body()` to get the raw body for signature verification.
Standard Python deployment practices: load the API key from environment variables (never commit), use a production-grade ASGI/WSGI server (uvicorn, gunicorn), configure retries and timeouts appropriately for your workload, and log SDK errors to your observability stack.
The SDK is built against Pydantic v2 and uses its model validation. For projects still on Pydantic v1, pin to the legacy SDK release (1.x line). Check the SDK changelog for migration notes between Pydantic versions.
Yes. The sync `SignBolt` client is safe to share across threads. Internally it uses `httpx.Client`, which is thread-safe. For async applications, create one `AsyncSignBolt` per event loop.
Yes. The SDK has minimal dependencies and initialises in under 100ms β safe for cold starts. Store the API key in Lambda environment variables or Secrets Manager. For heavy traffic, reuse the client across warm invocations by defining it at module level.
API access is included in the Business plan ($24/month). Upgrade to get your API key.