Sign10X API
A JSON REST API for creating documents, collecting signatures and reacting to events. Everything the product does, your software can do too.
Quick start
- 1Create an API key. Start in test mode. The secret is shown once — store it in your backend environment.
- 2Upload a document. POST /v1/documents with the file as multipart/form-data.
- 3Add your signers. Include recipients when you create the signature request.
- 4Create the signature request. POST /v1/signature-requests returns a draft you can still edit.
- 5Place fields. POST …/fields with normalized coordinates, or let detection suggest them.
- 6Send it. POST …/send returns a signing URL for every recipient.
- 7Embed or redirect. Open an embedded session in your own page, or redirect the signer.
- 8Receive the webhook. signature_request.completed arrives at your endpoint, signed.
- 9Download the signed PDF. GET …/completed-document returns a temporary download URL.
Authentication
Every request carries a secret key as a bearer token. Keys belong to one organization and one environment. Secrets are hashed before storage, shown only once at creation, and can be rotated or revoked at any time. Never expose a secret key in browser or mobile code — call the API from your own backend.
Authorization: Bearer sk_live_…
Content-Type: application/json
Idempotency-Key: 5f2c9d0a-… # optional, for safe retriesBase URL: /api/public/v1. Test keys never send real email and never produce billable documents.
/v1/documentsdocuments:writeCreate a document
Uploads a PDF, PNG or JPEG (max 25 MB) and creates a draft document. Send it as multipart/form-data.
| Parameter | Type | Description |
|---|---|---|
| file * | file | The document to upload. |
| name | string | Display name. Defaults to the file name. |
curl -X POST "/api/public/v1/v1/documents" \
-H "Authorization: Bearer $SIGN10X_API_KEY" \
-F "[email protected]" -F "name=Employment Agreement"{
"id": "doc_9f1c…",
"object": "document",
"name": "Employment Agreement",
"status": "draft",
"pages": 5,
"test_mode": true,
"created_at": "2026-04-02T10:12:31Z"
}Common errors: 401 unauthorized · 403 forbidden · 422 validation_error · 429 rate_limited
/v1/documents/{document_id}documents:readRetrieve a document
Returns one document belonging to your organization.
curl -X GET "/api/public/v1/v1/documents/{document_id}" \
-H "Authorization: Bearer $SIGN10X_API_KEY"{ "id": "doc_9f1c…", "object": "document", "status": "draft", "pages": 5 }Common errors: 404 not_found
/v1/documents/{document_id}/downloaddocuments:readDownload the original
Returns a short-lived signed download URL. Documents are stored privately and never have permanent public URLs.
curl -X GET "/api/public/v1/v1/documents/{document_id}/download" \
-H "Authorization: Bearer $SIGN10X_API_KEY"{ "download_url": "https://…", "expires_at": "2026-04-02T10:27:31Z" }/v1/documents/{document_id}documents:writeDelete a document
Deletes a draft document and its stored file. Sent or completed documents cannot be deleted.
curl -X DELETE "/api/public/v1/v1/documents/{document_id}" \
-H "Authorization: Bearer $SIGN10X_API_KEY"{ "id": "doc_9f1c…", "object": "document", "deleted": true }Common errors: 409 conflict
/v1/documents/{document_id}/analyze-fieldsdocuments:writeDetect empty fields
Deterministic detection of genuinely empty fields using PDF text extraction, coordinates, form controls, line/box geometry and label matching. No generative AI is used, and information already filled in is never returned as a field.
curl -X POST "/api/public/v1/v1/documents/{document_id}/analyze-fields" \
-H "Authorization: Bearer $SIGN10X_API_KEY"{
"document_id": "doc_9f1c…",
"status": "completed",
"fields": [
{ "type": "signature", "page": 4, "x": 0.62, "y": 0.81, "width": 0.25, "height": 0.06, "confidence": 0.98 },
{ "type": "date", "page": 4, "x": 0.62, "y": 0.88, "width": 0.2, "height": 0.05, "confidence": 0.96 }
]
}/v1/signature-requestssignature_requests:writeCreate a signature request
Creates a draft signature request with its recipients. Supports Idempotency-Key.
| Parameter | Type | Description |
|---|---|---|
| document_id * | string | Document to be signed. |
| name | string | Title shown to signers. |
| message | string | Message included with the invitation. |
| recipients * | array | name, email, role, order. |
| success_url | string | Where to send signers after completion. |
| cancel_url | string | Where to send signers who cancel. |
curl -X POST "/api/public/v1/v1/signature-requests" \
-H "Authorization: Bearer $SIGN10X_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "document_id": "doc_9f1c", "name": "Employment Agreement", "message": "Please review and sign.", "recipients": [ { "name": "John Smith", "email": "[email protected]", "role": "Employee", "order": 1 } ] }'{
"id": "sigreq_44a2…",
"object": "signature_request",
"status": "draft",
"document_id": "doc_9f1c…",
"recipients": [{ "id": "rec_71b0…", "name": "John Smith", "status": "pending" }]
}/v1/signature-requests/{id}/fieldssignature_requests:writeAdd fields to a recipient
Places fields using normalized coordinates (0–1 of page width/height, origin top-left) so they render correctly at any zoom. Types: signature, initials, date, name, email, text, checkbox, company, title, number.
curl -X POST "/api/public/v1/v1/signature-requests/{id}/fields" \
-H "Authorization: Bearer $SIGN10X_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "recipient_id": "rec_71b0", "fields": [ { "type": "signature", "page": 4, "x": 0.62, "y": 0.81, "width": 0.25, "height": 0.06 }, { "type": "date", "page": 4, "x": 0.62, "y": 0.88, "width": 0.2, "height": 0.05 } ] }'{ "signature_request_id": "sigreq_44a2…", "fields": [{ "id": "field_c81…", "type": "signature" }] }/v1/signature-requests/{id}/sendsignature_requests:writeSend for signature
Locks the request, generates cryptographically random signer tokens and signing URLs, records audit events and triggers webhooks.
curl -X POST "/api/public/v1/v1/signature-requests/{id}/send" \
-H "Authorization: Bearer $SIGN10X_API_KEY"{
"id": "sigreq_44a2…",
"status": "sent",
"recipients": [
{ "id": "rec_71b0…", "status": "pending", "signing_url": "https://…/sign/6b2f…" }
]
}Common errors: 409 conflict — already sent
/v1/signature-requests/{id}/voidsignature_requests:writeVoid a request
Cancels an in-flight request. Signing links stop working.
curl -X POST "/api/public/v1/v1/signature-requests/{id}/void" \
-H "Authorization: Bearer $SIGN10X_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "reason": "Superseded by a new version" }'{ "id": "sigreq_44a2…", "status": "voided" }/v1/signature-requests/{id}/statussignature_requests:readCheck status
Returns draft, sent, viewed, partially_signed, completed, declined, voided or expired, plus recipient-level status.
curl -X GET "/api/public/v1/v1/signature-requests/{id}/status" \
-H "Authorization: Bearer $SIGN10X_API_KEY"{
"status": "partially_signed",
"recipients": [
{ "name": "John Smith", "status": "completed" },
{ "name": "Jane Smith", "status": "pending" }
]
}/v1/signature-requests/{id}/embedded-sessionsignature_requests:writeCreate an embedded session
Mints a short-lived signing session for one recipient that you can render inside your own site in an iframe.
curl -X POST "/api/public/v1/v1/signature-requests/{id}/embedded-session" \
-H "Authorization: Bearer $SIGN10X_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "recipient_id": "rec_71b0", "expires_in": 3600 }'{
"session_id": "sess_2ab1…",
"url": "https://…/embed/sign/7c31…",
"expires_at": "2026-04-02T11:12:31Z"
}/v1/signature-requests/{id}/completed-documentdocuments:readDownload the signed PDF
Generates a flattened copy with every signature, initial, date and value burned in, and returns a temporary download URL. The original upload is never modified.
curl -X GET "/api/public/v1/v1/signature-requests/{id}/completed-document" \
-H "Authorization: Bearer $SIGN10X_API_KEY"{ "download_url": "https://…", "expires_at": "2026-04-02T10:27:31Z" }Common errors: 409 conflict — not completed yet
/v1/webhookswebhooks:writeCreate a webhook endpoint
Subscribes an https endpoint to events. The signing secret is returned once.
curl -X POST "/api/public/v1/v1/webhooks" \
-H "Authorization: Bearer $SIGN10X_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://customerwebsite.com/api/esign/webhook", "events": [ "signature_request.completed", "recipient.signed" ] }'{ "id": "whk_51c…", "url": "https://…", "secret": "whsec_…" }/v1/webhookswebhooks:readList webhook endpoints
Lists your endpoints with their last delivery status.
curl -X GET "/api/public/v1/v1/webhooks" \
-H "Authorization: Bearer $SIGN10X_API_KEY"{ "object": "list", "data": [{ "id": "whk_51c…", "active": true, "last_status": 200 }] }/v1/webhooks/{id}webhooks:writeDelete a webhook endpoint
Removes an endpoint and stops future deliveries.
curl -X DELETE "/api/public/v1/v1/webhooks/{id}" \
-H "Authorization: Bearer $SIGN10X_API_KEY"{ "id": "whk_51c…", "deleted": true }Webhook events & signatures
Each delivery includes X-Esign-Signature — an HMAC-SHA256 of the raw request body using your endpoint secret — plus X-Esign-Event and X-Esign-Delivery. Verify the signature before trusting a payload. Failed deliveries retry after 1, 5, 30, 120, 720 minutes.
import crypto from "node:crypto";
app.post("/api/esign/webhook", express.raw({ type: "application/json" }), (req, res) => {
const expected = crypto
.createHmac("sha256", process.env.SIGN10X_WEBHOOK_SECRET)
.update(req.body)
.digest("hex");
const received = req.get("X-Esign-Signature") ?? "";
const ok =
received.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
if (!ok) return res.status(401).send("invalid signature");
const event = JSON.parse(req.body.toString());
if (event.type === "signature_request.completed") markSigned(event.data.signature_request_id);
res.sendStatus(200); // respond within 10s; we retry on non-2xx
});Errors
{
"error": {
"type": "validation_error",
"message": "recipients[0].email must be a valid email address.",
"details": { "field": "recipients[0].email" },
"request_id": "req_8fc2…"
}
}| Status | Type | Meaning |
|---|---|---|
| 400 | invalid_request | The request could not be parsed. |
| 401 | unauthorized | Missing, malformed, expired or revoked API key. |
| 403 | forbidden | The key lacks the scope, or the resource belongs to another organization. |
| 404 | not_found | The resource does not exist. |
| 409 | conflict | The resource is not in a state that allows this action. |
| 422 | validation_error | A field failed validation. See `details`. |
| 429 | rate_limited | Too many requests. Retry after the window resets. |
| 500 | server_error | Something went wrong on our side. Retry safely with an idempotency key. |
Every response carries X-Request-ID. Quote it in support requests — it also appears in your request logs.
Rate limits & idempotency
Limits apply per API key, per minute, and are returned on every response as X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. On a 429, back off exponentially. Send an Idempotency-Key on creation calls so a retried request returns the original result instead of creating a duplicate.