API Reference
Complete reference for the zSign REST API. Send documents, manage signing sessions, and integrate webhooks.
Base URL
https://api.zsign.io/apiAuthentication
Bearer YOUR_API_KEYHealth & Status
API status and health check endpoints
Check API Status
/Verify the API is running.
Request
curl https://api.zsign.io/api
Response 200
API is running
{"message": "zSign API is running"}
Health Check
/healthGet detailed health status of the API.
Request
curl https://api.zsign.io/api/health
Response 200
Health status
{"status": "healthy"}
Documents
Upload, send, and manage documents
List Documents
/api/documentsRetrieve documents with optional filtering.
Query Parameters
| Name | Type | Description |
|---|---|---|
offset | integer= 0 | Pagination offset |
limit | integer= 20 | Items per page |
status | string | Filter by status |
recipient | string | Filter by recipient email |
Request
curl -X GET "https://api.zsign.io/api/documents?limit=20" \-H "Authorization: Bearer YOUR_API_KEY"
Response 200
List of documents
{"items": [{"id": "550e8400-e29b-41d4-a716-446655440000","name": "Contract.pdf","status": "pending","created_at": "2024-01-15T10:30:00Z"}],"total": 1,"offset": 0,"limit": 20}
Upload and Send Document
/api/documents/sendUpload a PDF and send it for signing in one request. PDFs must include field annotations using {type:party:name} syntax (e.g., {signature:signer}, {text:signer:name}). The party must exactly match a recipient's "role" — matching is case-sensitive, so {signature:signer} pairs with "role": "signer", not "Signer". Add * after type for required fields: {type*:party:name}. Supported types: signature, text, date, initials.
Request Body
Content-Type: multipart/form-data
Fields
| Name | Type | Description |
|---|---|---|
filerequired | file | PDF file to upload |
recipientsrequired | string | JSON array of recipients |
name | string | Document name |
[{"email": "john@example.com","name": "John Doe","role": "signer"}]
Request
curl -X POST https://api.zsign.io/api/documents/send \-H "Authorization: Bearer YOUR_API_KEY" \-F "file=@contract.pdf" \-F 'recipients=[{"email":"john@example.com","name":"John Doe","role":"signer"}]'
Response 201
Document sent successfully
{"document_id": "550e8400-e29b-41d4-a716-446655440000","session_id": "660e8400-e29b-41d4-a716-446655440000","signing_urls": [{"recipient_email": "john@example.com","signing_url": "https://app.zsign.io/sign/eyJ..."}]}
Signing Sessions
Create and manage signing sessions
Create Session
/api/sessionsCreate a new signing session.
Request Body
Content-Type: application/json
Fields
| Name | Type | Description |
|---|---|---|
document_idrequired | UUID | Document to sign |
document_typerequired | string | template or one_off |
recipientsrequired | array | List of recipients |
{"document_id": "550e8400-e29b-41d4-a716-446655440000","document_type": "one_off","recipients": [{"name": "John Doe","email": "john@example.com","role": "signer","signing_order": 1}]}
Request
curl -X POST https://api.zsign.io/api/sessions \-H "Authorization: Bearer YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"document_id": "550e8400-e29b-41d4-a716-446655440000","document_type": "one_off","recipients": [{"name": "John Doe","email": "john@example.com","role": "signer","signing_order": 1}]}'
Response 201
Session created
{"session_id": "550e8400-e29b-41d4-a716-446655440000","status": "pending","expires_at": "2024-02-14T10:30:00Z","recipients": [{"recipient_id": "770e8400-e29b-41d4-a716-446655440000","signing_url": "https://app.zsign.io/sign/eyJ..."}]}
Get Session Status
/api/sessions/{session_id}/statusGet lightweight session status.
Path Parameters
| Name | Type | Description |
|---|---|---|
session_idrequired | UUID | Session identifier |
Request
curl -X GET "https://api.zsign.io/api/sessions/{session_id}/status" \-H "Authorization: Bearer YOUR_API_KEY"
Response 200
Session status
{"session_id": "550e8400-e29b-41d4-a716-446655440000","status": "in_progress","progress": {"completed": 1,"total": 2,"percentage": 50}}
Webhooks
Configure webhook endpoints for real-time notifications
Create Webhook
/api/webhooksCreate or replace webhook configuration.
Request Body
Content-Type: application/json
Fields
| Name | Type | Description |
|---|---|---|
urlrequired | string | Webhook endpoint URL (HTTPS) |
eventsrequired | array | List of events to subscribe to |
{"url": "https://example.com/webhook","events": ["document.signed","document.completed"]}
Request
curl -X POST https://api.zsign.io/api/webhooks \-H "Authorization: Bearer YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"url": "https://example.com/webhook","events": ["document.signed", "document.completed"]}'
Response 201
Webhook created
{"id": "550e8400-e29b-41d4-a716-446655440000","url": "https://example.com/webhook","events": ["document.signed","document.completed"],"enabled": true,"secret": "whsec_..."}
White-Label Branding
Brand the signer-facing experience, subscribe to White-label ($49/month, 100 envelopes included), manage a custom sender domain, and configure embed origins
Get Branding Settings
/api/brandingRetrieve the current account's branding configuration.
Request
curl -H "Authorization: Bearer YOUR_API_KEY" https://api.zsign.io/api/branding
Response 200
Branding settings
{"id": "550e8400-e29b-41d4-a716-446655440000","user_id": "660e8400-e29b-41d4-a716-446655440000","company_name": "Acme Corp","logo_url": "https://storage.googleapis.com/zsign-public/branding/.../logo.png","primary_color": "#2563eb","custom_domain": null,"custom_domain_verified": false,"custom_domain_verified_at": null,"custom_domain_target": "zsign-frontend.onrender.com","custom_domain_a_record": "216.24.57.1","badge_removed": false,"embed_origins": ["https://app.acme.com"],"created_at": "2026-01-01T00:00:00Z","updated_at": "2026-01-01T00:00:00Z"}
Update Branding Settings
/api/brandingUpdate company name, logo URL, and accent color shown on signing pages, emails, and the Certificate of Completion.
Request Body
Content-Type: application/json
Fields
| Name | Type | Description |
|---|---|---|
company_name | string | Displayed on signing pages, emails, and the certificate |
logo_url | string | Public logo URL (or use POST /api/branding/logo to upload one) |
primary_color | string | Hex color, e.g. #2563eb, drives the signing page accent |
{"company_name": "Acme Corp","primary_color": "#ff6600"}
Request
curl -X PUT https://api.zsign.io/api/branding \-H "Authorization: Bearer YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"company_name": "Acme Corp", "primary_color": "#ff6600"}'
Response 200
Updated branding settings
{"company_name": "Acme Corp","primary_color": "#ff6600"}
Upload Brand Logo
/api/branding/logoUpload a PNG, JPEG, or WebP logo (max 1MB, magic-byte validated -- no SVG). Stores it in public GCS and saves the URL onto the account's branding.
Request Body
Content-Type: multipart/form-data
Fields
| Name | Type | Description |
|---|---|---|
filerequired | file | PNG, JPEG, or WebP image, max 1MB |
// multipart/form-data with a "file" field
Request
curl -X POST https://api.zsign.io/api/branding/logo \-H "Authorization: Bearer YOUR_API_KEY" \-F "file=@logo.png"
Response 200
Logo uploaded
{"logo_url": "https://storage.googleapis.com/zsign-public/branding/.../logo.png"}
Start White-Label Checkout
/api/branding/white-label/checkoutStart a $49/month White-label subscription via Stripe Checkout (100 envelopes included each month). Returns 409 if the account already has an active subscription.
Request
curl -X POST https://api.zsign.io/api/branding/white-label/checkout \-H "Authorization: Bearer YOUR_API_KEY"
Response 200
Stripe Checkout session URL
{"checkout_url": "https://checkout.stripe.com/c/pay/cs_test_..."}
Open Billing Portal
/api/branding/white-label/portalOpen the Stripe Billing Portal to manage or cancel the White-label subscription. Returns 400 if no subscription is on file.
Request
curl -X POST https://api.zsign.io/api/branding/white-label/portal \-H "Authorization: Bearer YOUR_API_KEY"
Response 200
Stripe Billing Portal session URL
{"portal_url": "https://billing.stripe.com/p/session/..."}
Set Sender Domain
/api/branding/sender-domainRegister a custom domain for outbound invite and completion emails via Resend. Requires an active White-label subscription (403 if not subscribed). Returns 409 if the domain is already claimed by another account.
Request Body
Content-Type: application/json
Fields
| Name | Type | Description |
|---|---|---|
domainrequired | string | Domain to send from, e.g. mail.yourcompany.com |
{"domain": "mail.yourcompany.com"}
Request
curl -X PUT https://api.zsign.io/api/branding/sender-domain \-H "Authorization: Bearer YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"domain": "mail.yourcompany.com"}'
Response 200
Sender domain registered with Resend, not yet verified
{"sender_domain": "mail.yourcompany.com","sender_domain_verified": false,"records": [{"record": "TXT","name": "mail.yourcompany.com","value": "resend-verify=..."}]}
Verify Sender Domain
/api/branding/sender-domain/verifyAsk Resend to re-check DNS for the account's configured sender domain and report status. Not gated by subscription status, so an account whose subscription has lapsed can still confirm its DNS records.
Request
curl -X POST https://api.zsign.io/api/branding/sender-domain/verify \-H "Authorization: Bearer YOUR_API_KEY"
Response 200
Current verification status and DNS records
{"sender_domain": "mail.yourcompany.com","sender_domain_verified": true,"records": [{"record": "TXT","name": "mail.yourcompany.com","value": "resend-verify=...","status": "verified"}]}
Remove Sender Domain
/api/branding/sender-domainRemove the configured sender domain and deregister it from Resend. Not gated by subscription status, so an account whose subscription has lapsed can still clean up its DNS.
Request
curl -X DELETE https://api.zsign.io/api/branding/sender-domain \-H "Authorization: Bearer YOUR_API_KEY"
Response 200
Sender domain removed
{"success": true}
Set Embed Origins
/api/branding/embed-originsConfigure the exact origins allowed to embed this account's signing pages in an iframe (default deny -- an empty list blocks all framing). Exact match only, https required except http://localhost[:port], no paths or wildcards, max 10.
Request Body
Content-Type: application/json
Fields
| Name | Type | Description |
|---|---|---|
originsrequired | array | List of allowed origins, e.g. ["https://app.yourcompany.com"] |
{"origins": ["https://app.yourcompany.com"]}
Request
curl -X PUT https://api.zsign.io/api/branding/embed-origins \-H "Authorization: Bearer YOUR_API_KEY" \-H "Content-Type: application/json" \-d '{"origins": ["https://app.yourcompany.com"]}'
Response 200
Updated branding settings including the normalized embed_origins list
{"embed_origins": ["https://app.yourcompany.com"]}