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

  1. 1Create an API key. Start in test mode. The secret is shown once — store it in your backend environment.
  2. 2Upload a document. POST /v1/documents with the file as multipart/form-data.
  3. 3Add your signers. Include recipients when you create the signature request.
  4. 4Create the signature request. POST /v1/signature-requests returns a draft you can still edit.
  5. 5Place fields. POST …/fields with normalized coordinates, or let detection suggest them.
  6. 6Send it. POST …/send returns a signing URL for every recipient.
  7. 7Embed or redirect. Open an embedded session in your own page, or redirect the signer.
  8. 8Receive the webhook. signature_request.completed arrives at your endpoint, signed.
  9. 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.

Request headers
Authorization: Bearer sk_live_…
Content-Type: application/json
Idempotency-Key: 5f2c9d0a-…   # optional, for safe retries

Base URL: /api/public/v1. Test keys never send real email and never produce billable documents.

POST/v1/documentsdocuments:write

Create a document

Uploads a PDF, PNG or JPEG (max 25 MB) and creates a draft document. Send it as multipart/form-data.

ParameterTypeDescription
file *fileThe document to upload.
namestringDisplay name. Defaults to the file name.
cURL — POST /v1/documents
curl -X POST "/api/public/v1/v1/documents" \
  -H "Authorization: Bearer $SIGN10X_API_KEY" \
  -F "[email protected]" -F "name=Employment Agreement"
Response
{
  "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

GET/v1/documents/{document_id}documents:read

Retrieve a document

Returns one document belonging to your organization.

cURL — GET /v1/documents/{document_id}
curl -X GET "/api/public/v1/v1/documents/{document_id}" \
  -H "Authorization: Bearer $SIGN10X_API_KEY"
Response
{ "id": "doc_9f1c…", "object": "document", "status": "draft", "pages": 5 }

Common errors: 404 not_found

GET/v1/documents/{document_id}/downloaddocuments:read

Download the original

Returns a short-lived signed download URL. Documents are stored privately and never have permanent public URLs.

cURL — GET /v1/documents/{document_id}/download
curl -X GET "/api/public/v1/v1/documents/{document_id}/download" \
  -H "Authorization: Bearer $SIGN10X_API_KEY"
Response
{ "download_url": "https://…", "expires_at": "2026-04-02T10:27:31Z" }
DELETE/v1/documents/{document_id}documents:write

Delete a document

Deletes a draft document and its stored file. Sent or completed documents cannot be deleted.

cURL — DELETE /v1/documents/{document_id}
curl -X DELETE "/api/public/v1/v1/documents/{document_id}" \
  -H "Authorization: Bearer $SIGN10X_API_KEY"
Response
{ "id": "doc_9f1c…", "object": "document", "deleted": true }

Common errors: 409 conflict

POST/v1/documents/{document_id}/analyze-fieldsdocuments:write

Detect 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 — POST /v1/documents/{document_id}/analyze-fields
curl -X POST "/api/public/v1/v1/documents/{document_id}/analyze-fields" \
  -H "Authorization: Bearer $SIGN10X_API_KEY"
Response
{
  "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 }
  ]
}
POST/v1/signature-requestssignature_requests:write

Create a signature request

Creates a draft signature request with its recipients. Supports Idempotency-Key.

ParameterTypeDescription
document_id *stringDocument to be signed.
namestringTitle shown to signers.
messagestringMessage included with the invitation.
recipients *arrayname, email, role, order.
success_urlstringWhere to send signers after completion.
cancel_urlstringWhere to send signers who cancel.
cURL — POST /v1/signature-requests
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 } ] }'
Response
{
  "id": "sigreq_44a2…",
  "object": "signature_request",
  "status": "draft",
  "document_id": "doc_9f1c…",
  "recipients": [{ "id": "rec_71b0…", "name": "John Smith", "status": "pending" }]
}
POST/v1/signature-requests/{id}/fieldssignature_requests:write

Add 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 — POST /v1/signature-requests/{id}/fields
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 } ] }'
Response
{ "signature_request_id": "sigreq_44a2…", "fields": [{ "id": "field_c81…", "type": "signature" }] }
POST/v1/signature-requests/{id}/sendsignature_requests:write

Send for signature

Locks the request, generates cryptographically random signer tokens and signing URLs, records audit events and triggers webhooks.

cURL — POST /v1/signature-requests/{id}/send
curl -X POST "/api/public/v1/v1/signature-requests/{id}/send" \
  -H "Authorization: Bearer $SIGN10X_API_KEY"
Response
{
  "id": "sigreq_44a2…",
  "status": "sent",
  "recipients": [
    { "id": "rec_71b0…", "status": "pending", "signing_url": "https://…/sign/6b2f…" }
  ]
}

Common errors: 409 conflict — already sent

POST/v1/signature-requests/{id}/voidsignature_requests:write

Void a request

Cancels an in-flight request. Signing links stop working.

cURL — POST /v1/signature-requests/{id}/void
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" }'
Response
{ "id": "sigreq_44a2…", "status": "voided" }
GET/v1/signature-requests/{id}/statussignature_requests:read

Check status

Returns draft, sent, viewed, partially_signed, completed, declined, voided or expired, plus recipient-level status.

cURL — GET /v1/signature-requests/{id}/status
curl -X GET "/api/public/v1/v1/signature-requests/{id}/status" \
  -H "Authorization: Bearer $SIGN10X_API_KEY"
Response
{
  "status": "partially_signed",
  "recipients": [
    { "name": "John Smith", "status": "completed" },
    { "name": "Jane Smith", "status": "pending" }
  ]
}
POST/v1/signature-requests/{id}/embedded-sessionsignature_requests:write

Create an embedded session

Mints a short-lived signing session for one recipient that you can render inside your own site in an iframe.

cURL — POST /v1/signature-requests/{id}/embedded-session
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 }'
Response
{
  "session_id": "sess_2ab1…",
  "url": "https://…/embed/sign/7c31…",
  "expires_at": "2026-04-02T11:12:31Z"
}
GET/v1/signature-requests/{id}/completed-documentdocuments:read

Download 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 — GET /v1/signature-requests/{id}/completed-document
curl -X GET "/api/public/v1/v1/signature-requests/{id}/completed-document" \
  -H "Authorization: Bearer $SIGN10X_API_KEY"
Response
{ "download_url": "https://…", "expires_at": "2026-04-02T10:27:31Z" }

Common errors: 409 conflict — not completed yet

POST/v1/webhookswebhooks:write

Create a webhook endpoint

Subscribes an https endpoint to events. The signing secret is returned once.

cURL — POST /v1/webhooks
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" ] }'
Response
{ "id": "whk_51c…", "url": "https://…", "secret": "whsec_…" }
GET/v1/webhookswebhooks:read

List webhook endpoints

Lists your endpoints with their last delivery status.

cURL — GET /v1/webhooks
curl -X GET "/api/public/v1/v1/webhooks" \
  -H "Authorization: Bearer $SIGN10X_API_KEY"
Response
{ "object": "list", "data": [{ "id": "whk_51c…", "active": true, "last_status": 200 }] }
DELETE/v1/webhooks/{id}webhooks:write

Delete a webhook endpoint

Removes an endpoint and stops future deliveries.

cURL — DELETE /v1/webhooks/{id}
curl -X DELETE "/api/public/v1/v1/webhooks/{id}" \
  -H "Authorization: Bearer $SIGN10X_API_KEY"
Response
{ "id": "whk_51c…", "deleted": true }

Webhook events & signatures

document.createddocument.completeddocument.deletedsignature_request.createdsignature_request.sentsignature_request.viewedsignature_request.partially_signedsignature_request.completedsignature_request.declinedsignature_request.voidedsignature_request.expiredrecipient.sentrecipient.viewedrecipient.signedrecipient.declinedsignature.created

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.

Verify a delivery (Node)
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 shape
{
  "error": {
    "type": "validation_error",
    "message": "recipients[0].email must be a valid email address.",
    "details": { "field": "recipients[0].email" },
    "request_id": "req_8fc2…"
  }
}
StatusTypeMeaning
400invalid_requestThe request could not be parsed.
401unauthorizedMissing, malformed, expired or revoked API key.
403forbiddenThe key lacks the scope, or the resource belongs to another organization.
404not_foundThe resource does not exist.
409conflictThe resource is not in a state that allows this action.
422validation_errorA field failed validation. See `details`.
429rate_limitedToo many requests. Retry after the window resets.
500server_errorSomething 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.