REST API reference
Everything you can do to your Signadoc account over HTTPS. Examples use placeholder keys; sign in to see them filled in with your own. New here? Read the integration overview first.
Getting started
Base URL: https://signadoc.app. All endpoints are under /api, accept and return JSON (except uploads, which are multipart, and PDF downloads), and use conventional HTTP status codes. IDs are prefixed so you can tell them apart: doc_ documents, env_ envelopes, fld_ fields, emb_ embed tokens.
Your first call, which lists your templates:
curl https://signadoc.app/api/documents -H "X-Api-Key: sk_YOUR_SECRET_KEY"
There are no webhooks yet. To learn when a document is signed, poll GET /api/envelopes/:id or, when embedding the signing page, listen for the signed event.
Authentication
Send your secret key with every request, either as an X-Api-Key header or as a bearer token. Keep it on your server: it grants full access to your account.
curl https://signadoc.app/api/envelopes -H "X-Api-Key: sk_YOUR_SECRET_KEY" curl https://signadoc.app/api/envelopes -H "Authorization: Bearer sk_YOUR_SECRET_KEY"
The publishable key (pk_…) is different: it is safe in a web page but can only open the embedded field designer from your allowed origins. It cannot read or create anything through the API.
Embed tokens (emb_…) are scoped, expiring credentials created by POST /api/embed/sessions. The embedded pages send them as X-Embed-Token; you normally never handle them directly.
Errors
Every error is JSON with a human-readable error message, sometimes with extra machine-readable fields:
{ "error": "Missing values for API-filled fields: Company", "missing": ["Company"], "hint": "Pass them in \"values\" or set \"allow_missing\": true to leave them blank." }
{ "error": "A document named \"NDA\" already exists. Choose a different name.", "code": "name_taken", "document_id": "doc_…" }
{ "error": "The Free plan allows 3 signing requests per month. Upgrade to Pro for unlimited signing.", "code": "plan_limit", "limit": "envelopes_per_month", "upgrade_url": "/app/billing" }| Status | Meaning |
|---|---|
400 | The request is malformed or missing something. The message says what. |
401 | No valid key. Check the X-Api-Key header. |
402 | A plan limit was reached (code: plan_limit). The message names the limit and upgrade_url points to billing. |
403 | The credential is not allowed to do this (for example an embed token calling an account-wide endpoint). |
404 | Unknown ID, or the resource belongs to another account. |
409 | Conflict with current state: duplicate document name, or changing an envelope that is no longer pending. |
413 | The upload or request body is too large for your plan. |
500 | Something failed on our side. Retrying is safe for GET requests. |
Documents
A document is an uploaded PDF plus its field layout. Upload once, reuse it as a template for every signer.
/api/documentsList your documents.
curl https://signadoc.app/api/documents -H "X-Api-Key: sk_YOUR_SECRET_KEY"
{ "documents": [ { "id": "doc_…", "name": "Service Agreement", "page_count": 1, "tags": ["Sales"], "field_count": 6,
"envelope_count": 12, "completed_count": 9, "pending_count": 2, "created_at": "…", "updated_at": "…" } ] }/api/documentsUpload a PDF. Multipart form, not JSON.
| Parameter | Type | Description |
|---|---|---|
file | file | The PDF. Required. |
name | string | Display name. Must be unique on your account (case-insensitive). Defaults to the file name. |
tags | string | Comma-separated tags, e.g. "Sales, HR". |
external_ref | string | Your own identifier, stored and returned as-is. |
fields | JSON string | Optional field list, same shape as PUT /fields, to define fields in the same call. |
curl -X POST https://signadoc.app/api/documents -H "X-Api-Key: sk_YOUR_SECRET_KEY" \ -F "file=@agreement.pdf" -F "name=Service Agreement" -F "tags=Sales"
201 { "document": { "id": "doc_…", "name": "Service Agreement", "page_count": 1, "pages": [ { "width": 612, "height": 792, "rotation": 0 } ],
"tags": ["Sales"], "sha256": "…", "size": 18271, "fields": [] } }
409 { "error": "A document named \"Service Agreement\" already exists…", "code": "name_taken", "document_id": "doc_…" }/api/documents/:idOne document with page sizes and its fields.
curl https://signadoc.app/api/documents/DOC_ID -H "X-Api-Key: sk_YOUR_SECRET_KEY"
{ "document": { "id": "doc_…", "name": "…", "pages": [ { "width": 612, "height": 792 } ], "fields": [ { "key": "Name", "type": "name", "page": 1, "x": 90, "y": 150, "w": 200, "h": 18, "required": true, "fill": "signer" } ] } }/api/documents/:id/fileDownload the original PDF.
curl https://signadoc.app/api/documents/DOC_ID/file -H "X-Api-Key: sk_YOUR_SECRET_KEY" -o original.pdf
/api/documents/:idRename a document and/or replace its tags.
| Parameter | Type | Description |
|---|---|---|
name | string | New name (unique per account). |
tags | string[] | Full replacement tag list. Pass [] to clear. |
curl -X PATCH https://signadoc.app/api/documents/DOC_ID -H "X-Api-Key: sk_YOUR_SECRET_KEY" -H "Content-Type: application/json" -d '{ "tags": ["Sales", "2026"] }'{ "document": { "id": "doc_…", "name": "…", "tags": ["Sales", "2026"] } }/api/documents/:idDelete the document, its files and every envelope created from it. Irreversible.
curl -X DELETE https://signadoc.app/api/documents/DOC_ID -H "X-Api-Key: sk_YOUR_SECRET_KEY"
{ "ok": true }Fields
Fields are the boxes on the page. Each has a key (what you pass in values), a type, a position in PDF points measured from the top-left of the page, and a fill mode that says who supplies the value.
/api/documents/:id/fieldsThe field list.
curl https://signadoc.app/api/documents/DOC_ID/fields -H "X-Api-Key: sk_YOUR_SECRET_KEY"
{ "fields": [ { "id": "fld_…", "key": "Name", "label": "Name", "type": "name", "page": 1, "x": 90, "y": 150, "w": 200, "h": 18, "required": true, "font_size": 11, "fill": "signer", "auto_source": null } ] }/api/documents/:id/fieldsReplace all fields. Send the complete list every time.
| Parameter | Type | Description |
|---|---|---|
key | string | Name used in values. Required. Repeating a key puts the same value in several places. |
label | string | Shown to the signer. Defaults to the key. |
type | string | name, address, phone, email, date, text, signature, initials, checkbox. |
page | number | 1-based page number. |
x, y, w, h | number | Position and size in PDF points from the top-left corner. |
required | boolean | Default true. |
font_size | number | Points. Default 11. Ignored for signature, initials, checkbox. |
fill | string | signer (default), api, or auto. See below. |
auto_source | string | For fill=auto: date, signer_name, signer_email, envelope_id, document_name. |
curl -X PUT https://signadoc.app/api/documents/DOC_ID/fields -H "X-Api-Key: sk_YOUR_SECRET_KEY" -H "Content-Type: application/json" \
-d '{ "fields": [ { "key": "Name", "type": "name", "page": 1, "x": 90, "y": 150, "w": 200, "h": 18, "fill": "api" },
{ "key": "Signature", "type": "signature", "page": 1, "x": 90, "y": 640, "w": 200, "h": 44 } ] }'{ "fields": [ … ] }Fill modes
| fill | Who supplies the value | What the signer sees |
|---|---|---|
signer | The signer types it. If you pre-fill it through values it is locked by default (pass locked: false or a list of keys to keep it editable). | An input, or read-only text if pre-filled and locked. |
api | Your system, in values when the envelope is created. Required unless allow_missing: true. | Read-only text. |
auto | Signadoc, at the moment of signing, from auto_source. | A read-only preview. |
Envelopes (signing requests)
An envelope is one document sent to one signer. It carries the pre-filled values, the private signing link, its status (pending, completed or void), an audit trail, and the signed PDF once done.
/api/documents/:id/envelopesCreate a signing request for one person.
| Parameter | Type | Description |
|---|---|---|
signer_name | string | Shown on the signing page and in the certificate. |
signer_email | string | Needed to email the link. Falls back to values.Email. |
values | object | Field key → value. Stamped into the PDF. Unknown keys are ignored; signature fields cannot be set here. |
locked | boolean | string[] | true (default) locks every pre-filled field; false leaves them editable; an array locks only those keys. API-filled fields are always locked. |
allow_missing | boolean | Allow required api-filled fields to be left blank. |
redirect_url | string | Where to send the signer after signing (http/https). |
external_ref | string | Your own identifier, returned as-is. |
send | boolean | true emails the link immediately (needs a plan with email and SMTP configured). |
subject, message | string | Email subject and intro text when send is true. |
curl -X POST https://signadoc.app/api/documents/DOC_ID/envelopes -H "X-Api-Key: sk_YOUR_SECRET_KEY" -H "Content-Type: application/json" \
-d '{ "signer_name": "Jane Doe", "signer_email": "jane@example.com",
"values": { "Name": "Jane Doe", "Phone": "555-0100" }, "redirect_url": "https://example.com/thanks", "send": true }'201 { "envelope": { "id": "env_…", "status": "pending", "sign_url": "https://signadoc.app/sign/…", "embed_sign_url": "…?embed=1", "values": { … }, "locked": ["Name", "Phone"], "signed_pdf_url": null }, "delivery": { "sent": true, "mode": "smtp" } }
400 { "error": "Missing values for API-filled fields: Company", "missing": ["Company"], "hint": "…" }
402 { "error": "The Free plan allows 3 signing requests per month…", "code": "plan_limit", "upgrade_url": "/app/billing" }/api/documents/:id/sendSend to up to 200 recipients in one call. Each gets their own envelope.
| Parameter | Type | Description |
|---|---|---|
recipients | array | [{ name, email, values, external_ref, redirect_url }]. Required. |
values | object | Shared values applied to every recipient (per-recipient values win). |
subject, message | string | Email content. |
send | boolean | Default true. false creates the envelopes without emailing. |
locked, redirect_url, external_ref, allow_missing | As for a single envelope. |
curl -X POST https://signadoc.app/api/documents/DOC_ID/send -H "X-Api-Key: sk_YOUR_SECRET_KEY" -H "Content-Type: application/json" \
-d '{ "recipients": [ { "name": "Jane Doe", "email": "jane@example.com" }, { "name": "Bob Ray", "email": "bob@example.com" } ],
"values": { "Company": "Acme LLC" }, "subject": "Please sign" }'{ "created": 2, "sent": 2, "results": [ { "email": "jane@example.com", "envelope": { "id": "env_…", "sign_url": "…" }, "delivery": { "sent": true } }, { "email": "bad-address", "error": "Invalid email address" } ] }/api/documents/:id/fillFill the PDF with values and return it. No envelope, no signing.
| Parameter | Type | Description |
|---|---|---|
values | object | Field key → value. |
signer_name, signer_email | string | Used by auto fields that reference the signer. |
curl -X POST https://signadoc.app/api/documents/DOC_ID/fill -H "X-Api-Key: sk_YOUR_SECRET_KEY" -H "Content-Type: application/json" -d '{ "values": { "Name": "Jane Doe" } }' -o filled.pdfThe PDF bytes (Content-Type: application/pdf).
/api/documents/:id/envelopesEnvelopes created from one document.
curl https://signadoc.app/api/documents/DOC_ID/envelopes -H "X-Api-Key: sk_YOUR_SECRET_KEY"
{ "envelopes": [ … ] }/api/envelopesAll envelopes on the account, newest first (max 500).
| Parameter | Type | Description |
|---|---|---|
status | query | pending, completed or void. |
curl "https://signadoc.app/api/envelopes?status=pending" -H "X-Api-Key: sk_YOUR_SECRET_KEY"
{ "envelopes": [ { "id": "env_…", "document_name": "…", "status": "pending", "signer_email": "…", "sign_url": "…", "sent_at": "…", "send_count": 1 } ] }/api/envelopes/:idStatus, values and the full audit trail. Poll this to know when a document is signed.
curl https://signadoc.app/api/envelopes/ENV_ID -H "X-Api-Key: sk_YOUR_SECRET_KEY"
{ "envelope": { "id": "env_…", "status": "completed", "completed_at": "…", "signed_sha256": "…", "signed_pdf_url": "https://signadoc.app/api/envelopes/env_…/signed.pdf",
"audit": [ { "event": "envelope.created", "at": "…" }, { "event": "envelope.sent", "detail": { "to": "…" } }, { "event": "envelope.viewed", "ip": "203.0.113.9", "user_agent": "…" },
{ "event": "envelope.signed" }, { "event": "envelope.completed" } ] } }
Audit events: envelope.created, envelope.updated, envelope.sent, envelope.send_failed, envelope.viewed, envelope.signed, envelope.completed, envelope.voided./api/envelopes/:idChange values or signer details on a pending envelope, for example when your CRM learns a phone number after sending.
| Parameter | Type | Description |
|---|---|---|
values | object | Keys to set or overwrite. |
clear | string[] | Keys to remove. |
locked | boolean | string[] | As on creation. |
signer_name, signer_email | string |
curl -X PATCH https://signadoc.app/api/envelopes/ENV_ID -H "X-Api-Key: sk_YOUR_SECRET_KEY" -H "Content-Type: application/json" -d '{ "values": { "Phone": "555-0199" } }'{ "envelope": { … } } 409 if the envelope is no longer pending./api/envelopes/:id/sendEmail the signing link, or send a reminder.
| Parameter | Type | Description |
|---|---|---|
to | string | Override the recipient address. |
subject, message | string | Email content. |
curl -X POST https://signadoc.app/api/envelopes/ENV_ID/send -H "X-Api-Key: sk_YOUR_SECRET_KEY" -H "Content-Type: application/json" -d '{ "message": "Friendly reminder!" }'{ "delivery": { "sent": true, "mode": "smtp" }, "envelope": { "sent_at": "…", "send_count": 2 } }/api/envelopes/:id/signed.pdfDownload the signed, flattened PDF with its certificate page. 404 until signed.
| Parameter | Type | Description |
|---|---|---|
download | query | download=1 sets Content-Disposition: attachment. |
curl https://signadoc.app/api/envelopes/ENV_ID/signed.pdf -H "X-Api-Key: sk_YOUR_SECRET_KEY" -o signed.pdf
/api/envelopes/:id/voidCancel a pending request. The link stops working. Completed envelopes cannot be voided.
| Parameter | Type | Description |
|---|---|---|
reason | string | Stored in the audit trail. |
curl -X POST https://signadoc.app/api/envelopes/ENV_ID/void -H "X-Api-Key: sk_YOUR_SECRET_KEY" -H "Content-Type: application/json" -d '{ "reason": "Superseded" }'{ "envelope": { "status": "void" } }/api/envelopes/:idDelete an envelope and its signed PDF.
curl -X DELETE https://signadoc.app/api/envelopes/ENV_ID -H "X-Api-Key: sk_YOUR_SECRET_KEY"
{ "ok": true }Embed sessions
Short-lived tokens that let an iframe act on your account without exposing a key. See the Overview for how the iframe side works.
/api/embed/sessionsCreate an embed session with your secret key.
| Parameter | Type | Description |
|---|---|---|
kind | string | define (field designer, default) or sign (signing page). |
document_id | string | define: restrict the session to this document. sign: create an envelope from this document. |
envelope_id | string | sign: embed an existing envelope instead. |
values, signer_name, signer_email, redirect_url, external_ref | sign: passed to envelope creation. | |
title, accent, allow_upload | string | define: designer appearance options. |
ttl_seconds | number | 60 to 86400. Default 3600. |
curl -X POST https://signadoc.app/api/embed/sessions -H "X-Api-Key: sk_YOUR_SECRET_KEY" -H "Content-Type: application/json" -d '{ "kind": "sign", "document_id": "DOC_ID", "signer_name": "Jane Doe", "values": { "Name": "Jane Doe" } }'define → 201 { "kind": "define", "token": "emb_…", "url": "https://signadoc.app/embed/define?token=emb_…", "expires_in": 3600 }
sign → 201 { "kind": "sign", "envelope": { … }, "url": "https://signadoc.app/sign/…?embed=1", "expires_in": 3600 }/api/embed/sessions/publicSame, but callable from the browser with the publishable key. Origin-checked; define only.
| Parameter | Type | Description |
|---|---|---|
publishable_key | string | Your pk_ key. |
document_id | string | Optional. |
fetch('https://signadoc.app/api/embed/sessions/public', { method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ publishable_key: 'pk_YOUR_PUBLISHABLE_KEY' }) }).then(r => r.json()).then(({ url }) => iframe.src = url);Account
Mostly used by the dashboard, but available to scripts.
/api/auth/meThe current user, plan and keys.
curl https://signadoc.app/api/auth/me -H "X-Api-Key: sk_YOUR_SECRET_KEY"
{ "user": { "id": "usr_…", "email": "…", "name": "…" }, "plan": { "id": "pro", … }, "secret_key": "sk_…", "publishable_key": "pk_…", "allowed_origins": [ … ] }/api/auth/settingsUpdate your name and allowed embed origins.
| Parameter | Type | Description |
|---|---|---|
name | string | |
allowed_origins | string[] | Origins allowed to host the embedded designer. |
curl -X PUT https://signadoc.app/api/auth/settings -H "X-Api-Key: sk_YOUR_SECRET_KEY" -H "Content-Type: application/json" -d '{ "allowed_origins": ["https://portal.example.com"] }'/api/auth/rotate-keysGenerate a new secret or publishable key. The old one stops working immediately.
| Parameter | Type | Description |
|---|---|---|
key | string | "secret" or "publishable". |
curl -X POST https://signadoc.app/api/auth/rotate-keys -H "X-Api-Key: sk_YOUR_SECRET_KEY" -H "Content-Type: application/json" -d '{ "key": "secret" }'Objects
Document
{ "id": "doc_…", "name": "Service Agreement", "original_name": "agreement.pdf", "page_count": 2, "size": 18271, "sha256": "…",
"source": "app" | "api" | "embed", "external_ref": null, "tags": ["Sales"], "created_at": "…", "updated_at": "…",
"pages": [ { "width": 612, "height": 792, "rotation": 0 } ], // on GET /api/documents/:id
"fields": [ Field, … ] }Field
{ "id": "fld_…", "key": "Name", "label": "Full name", "type": "name", "page": 1, "x": 90, "y": 150, "w": 200, "h": 18,
"required": true, "font_size": 11, "fill": "signer" | "api" | "auto", "auto_source": null | "date" | "signer_name" | "signer_email" | "envelope_id" | "document_name" }Envelope
{ "id": "env_…", "document_id": "doc_…", "document_name": "…", "status": "pending" | "completed" | "void",
"signer_name": "Jane Doe", "signer_email": "jane@example.com", "values": { "Name": "Jane Doe" }, "locked": ["Name"],
"redirect_url": null, "external_ref": null, "created_at": "…", "completed_at": null, "sent_at": null, "send_count": 0,
"sign_url": "https://signadoc.app/sign/…", "embed_sign_url": "https://signadoc.app/sign/…?embed=1",
"signed_pdf_url": null, "signed_sha256": null,
"audit": [ { "event": "envelope.created", "ip": "…", "user_agent": "…", "detail": {}, "at": "…" } ] // on GET /api/envelopes/:id
}Values
A plain object of field key to value. Text-like fields take strings, checkboxes take true/false (or "yes"/"on"), dates take any string you want printed. Keys that do not match a field are ignored. Signature and initials fields can only be drawn by the signer.