ZeroDocs API Reference
Integrate e-signatures, PDF tools, and AI auto-fill directly into your product. All requests and responses are JSON unless noted.
Authentication
Include your secret API key in the Authorization header on every request. Keys are prefixed zd_live_ in production or zd_test_ in sandbox.
Rate limits
Per API key per minute. Exceeded requests receive 429 with a Retry-After header in seconds.
| Endpoint group | Limit |
|---|---|
| General | 300 req/min |
| Document create/send | 60 req/min |
| Bulk send | 10 jobs/min |
| OCR submit | 30 req/min |
| AI endpoints | 20 req/min |
Error codes
All errors return { "error": { "code": "...", "message": "..." } }.
| Code | HTTP | Description |
|---|---|---|
| INVALID_API_KEY | 401 | Key missing, malformed, or revoked |
| QUOTA_EXCEEDED | 402 | Monthly doc or OCR quota reached |
| DOCUMENT_NOT_FOUND | 404 | Doc ID doesn't exist or belongs to another account |
| DOCUMENT_COMPLETED | 422 | Action not allowed on a completed document |
| VALIDATION_ERROR | 422 | Body failed validation — see error.fields |
| RATE_LIMITED | 429 | Too many requests — check Retry-After |
| INTERNAL_ERROR | 500 | Server error — safe to retry with backoff |
Documents
Paginated list of documents, newest first.
| Query param | Type | Description |
|---|---|---|
| statusoptional | string | pending, completed, declined, voided, expired |
| limitoptional | integer | 1–100, default 20 |
| cursoroptional | string | Pagination cursor from previous response |
| created_afteroptional | ISO 8601 | Filter documents created after this datetime |
Upload a PDF and send signing requests. Request must be multipart/form-data.
| Field | Type | Description |
|---|---|---|
| filerequired | file | PDF, max 25MB |
| titlerequired | string | Document display name |
| signersrequired | array | Array of signer objects: [{"name":"...","email":"...","order":1}] |
| messageoptional | string | Custom email message to signers |
| expires_atoptional | ISO 8601 | Expiry date (default: 30 days from now) |
| reminder_daysoptional | integer | Auto-reminder interval in days (default: 3) |
Immediately invalidates all signer links. Cannot void completed documents.
| Body | Type | Description |
|---|---|---|
| reasonoptional | string | Reason communicated to signers via email |
Returns signed PDF with embedded audit trail as application/pdf. Only available when status is completed.
Bulk Send
Creates a job from a template. Each recipient gets a personalised document. Response includes a job_id — poll GET /bulk-jobs/{id} for progress, or use webhooks.
| Field | Type | Description |
|---|---|---|
| template_idrequired | string | Template to use for personalisation |
| recipientsrequired | array | Up to 500 recipient objects with token values |
| subjectoptional | string | Email subject — supports {"{"}"{"}"} token substitution |
| send_atoptional | ISO 8601 | Schedule send time (default: immediate) |
| expires_atoptional | ISO 8601 | Expiry for all documents in the job |
Returns job progress including sent, failed, and total counts. Status is queued, running, completed, or failed.
OCR
Async text extraction via AWS Textract. Deducts from your OCR page quota. Use webhook_url or poll GET /ocr/jobs/{id}.
| Field | Type | Description |
|---|---|---|
| filerequired | file | PDF file, max 25MB |
| webhook_urloptional | URL | Called with result when complete |
Webhooks
ZeroDocs POSTs JSON to your webhook_url when events occur. Verify the X-ZeroDocs-Signature header (HMAC-SHA256 of request body using your webhook secret).
| Event type | Description |
|---|---|
| document.signed | A signer completed their fields |
| document.completed | All signers signed — PDF ready |
| document.declined | A signer declined to sign |
| document.voided | Document was voided |
| document.expired | Document passed expiry date |
| bulk_job.completed | Bulk send job finished |
| ocr.completed | OCR job finished |
curl -X POST \
https://api.zerodocs.xyz/v1/documents \
-H "Authorization: Bearer $ZD_KEY" \
-F "file=@agreement.pdf" \
-F "title=Service Agreement" \
-F 'signers=[{"name":"Rahul","email":"r@co.in"}]'
import hmac, hashlib, os
def verify(body, sig):
secret = os.environ['ZD_SECRET'].encode()
expected = hmac.new(secret, body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, sig)
{
"id": "doc_a1b2c3d4e5",
"title": "Service Agreement",
"status": "pending",
"created_at": "2026-02-28T11:43:00Z",
"expires_at": "2026-03-30T11:43:00Z",
"signers": [{
"id": "sgn_x1y2z3",
"name": "Rahul Sharma",
"email": "r@acme.co",
"status": "pending",
"signed_at": null
}],
"download_url": null
}