eSign API Documentation
Programmatically send documents for signature using the REST API.
Authentication
All API requests require a Bearer token. Create an API key from Settings.
curl -X POST https://www.switchlabs.dev/api/esign/v1/documents \ -H "Authorization: Bearer esk_your_api_key_here" \ -H "Content-Type: multipart/form-data" \ ...
Base URL
https://www.switchlabs.dev/api/esign/v1
Endpoints
/documentsCreate a new document and optionally send it immediately. The API also accepts JSON payloads withfileBase64,templateId, per-envelopewebhooks, and embedded recipients viaclientUserId /deliveryMethod.
Form Data Parameters
| Parameter | Type | Description |
|---|---|---|
| file | File | PDF file (required) |
| title | string | Document title (required) |
| recipients | JSON | Array of {name, email, role?} (required) |
| fields | JSON | Array of field placements (optional) |
| signing_order | string | "parallel" or "sequential" |
| send | string | "true" to send immediately |
Field Placement Format
{
"recipient_index": 0, // Index into recipients array
"type": "signature", // signature, initials, text, date, date_signed, checkbox
"page_index": 0, // Zero-based page number
"x": 0.1, // Normalized position (0-1)
"y": 0.8,
"width": 0.2,
"height": 0.05,
"required": true,
"font_size": 10 // Optional, text/date fields (6-36, default 14)
}Example
curl -X POST https://www.switchlabs.dev/api/esign/v1/documents \
-H "Authorization: Bearer esk_..." \
-F "file=@contract.pdf" \
-F "title=Service Agreement" \
-F 'recipients=[{"name":"John","email":"john@example.com"}]' \
-F 'fields=[{"recipient_index":0,"type":"signature","page_index":0,"x":0.1,"y":0.85,"width":0.25,"height":0.05}]' \
-F "send=true"Response
{
"id": "abc123...",
"status": "sent",
"recipients": [
{
"id": "def456...",
"signing_url": "https://www.switchlabs.dev/sign/..."
}
]
}Recipient access codes
Create the document as a draft, configure each recipient's access code, then send it. Codes can only be changed before sending. Share the code separately; it is never included in a signing invitation or returned by the API.
PUT /documents/{documentId}/recipients/{recipientId}/access
Content-Type: application/json
{ "accessCode": "your-separately-shared-code" }
Response: { "accessCodeRequired": true }Use 8–64 case-sensitive characters without leading or trailing spaces. Send { "accessCode": null } to remove a draft's code. GET on the same endpoint returns only whether a code is required. PUT requires the documents:write scope; GET requires documents:read. The Business API entitlement and document ownership checks apply.
Recipients must enter the code before opening or signing the document. Failed attempts are limited. An access code establishes knowledge of that code; it does not verify a government identity. The completion certificate records the authentication method and accepted electronic-record disclosure version.
/documentsList your documents with optional filtering.
Query Parameters
| status | Filter by status (draft, sent, completed, etc.) |
| limit | Results per page (max 100, default 50) |
| offset | Pagination offset |
/documents/{id}/statusGet document status with recipient signing details.
/documents/{id}/download?file=completedDownload PDF bytes. file iscompleted (default, the signed PDF),certificate (certificate of completion), ororiginal. Completed and certificate return 409 until every signer has finished. The signed PDF's SHA-256 is returned inX-ESign-Document-Hash.
/documents/{id}/sendSend a draft document for signing.
// Optional body
{
"message": "Please sign at your earliest convenience"
}PowerForms
Templates can be published as public self-service URLs from the Templates UI. A PowerForm creates a new document from the template, sends it immediately, and can redirect the first signer directly into signing.
GET https://sign.switchlabs.dev/f/{slug}
POST /api/esign/powerforms/{slug}
{
"title": "Optional document title override",
"recipients": [
{ "name": "Ada Lovelace", "email": "ada@example.com" }
]
}Webhooks
Configure webhooks from Settings to receive real-time notifications when documents are signed, completed, or declined.
Available Events
| document.created | Document was created |
| document.sent | Document was sent for signing |
| document.completed | All parties signed |
| document.declined | A signer declined |
| document.voided | Document was voided by sender |
| recipient.signed | A recipient completed signing |
| recipient.viewed | A recipient viewed the document |
| recipient.declined | A recipient declined to sign |
| reminder.sent | A reminder was sent |
Payload Format
{
"event": "recipient.signed",
"document_id": "abc123...",
"recipient_id": "def456...",
"timestamp": "2026-03-26T12:00:00.000Z",
"data": {
"fieldCount": 3
}
}Signature Verification
Each webhook includes an X-ESign-Signature header with an HMAC-SHA256 signature of the payload body using your webhook secret.
// Node.js verification
const crypto = require('crypto');
function verifyWebhook(body, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}Rate Limits
| Plan | Docs/Month | Recipients/Doc | API Access |
|---|---|---|---|
| Free | 3 | 2 | No |
| Pro | Unlimited | 10 | No |
| Business | Unlimited | Unlimited | Yes |