Core Concepts
The SmartDocs API is organized around a small set of resources. This page explains how they relate so the reference reads naturally.
Organizations
Everything you create belongs to your organization — templates, PDF assets, signing processes, and API keys are all org-scoped, and an API key can only see its own organization's data.
Access inside an organization is role-based: OWNER, ADMIN, and MEMBER. API keys carry either
ADMIN or MEMBER (never OWNER); required roles vary by endpoint — managing templates and PDF
assets, and creating direct signing processes, require ADMIN, while reading data and starting
signings from a published template are available to MEMBER keys. The required role for each
operation is noted in the API reference. Plans (FREE, PRO, ENTERPRISE) set quotas — storage and monthly
signing volume — and gate features such as SMS-verified signing. Exceeding a
plan quota returns a 402 with a stable error code (see Conventions).
Templates
A template is a reusable document definition. The PDF-oriented /templates resource supports two kinds:
PDF— built on an uploaded PDF, with signing fields placed on its pages.HTML— rendered to PDF from an HTML layout with dynamic input data.
The important difference is where the document bytes come from and when field geometry is known:
| Kind | Source document | How document data is filled | How signer fields are placed |
|---|---|---|---|
PDF | An uploaded, processed PDF asset. | The PDF field editor's Sender fields become inputSchema + prefillFields; data prints those values onto the PDF. | The PDF field editor's Signer fields store fixed page coordinates (page, x, y, w, h). |
HTML | Stored HTML and CSS rendered to PDF at start time. | The HTML editor's variables marked asked at start become inputSchema; data fills Liquid such as {{ customer.name }}. | The HTML editor's signer fields bind to <span data-sd-field="..."> markers; coordinates are measured from the rendered PDF. |
A template moves through three statuses: DRAFT → PUBLISHED → ARCHIVED. The draft is mutable —
you edit it via PATCH /templates/{id}/draft. Publishing snapshots the draft into a template
version: an immutable, numbered record of everything needed to start a signing. Already-published
versions never change; publishing again creates the next version, and in-flight signing processes
keep pointing at the exact version they were started from.
The draft, unpublished changes included, is returned only to callers that may edit templates. For
everyone else, MEMBER keys included, draft is null: read latestPublishedVersion for what can
be started.
A version's definition contains:
- Signer roles — the named parties (each with a stable role key) that must sign.
- Input schema — the dynamic data your integration supplies at start time (rendered into HTML templates and prefillable fields).
- Prefill fields — PDF-template text overlays fed by input data. These become part of the generated source PDF and are not editable by the signer.
- Signing fields — typed boxes (
TEXT,DATE,CHECKBOX,SIGNATURE) placed on the document and assigned to a role. These are the fields the signer fills or confirms.
POST /templates/{id}/start-signing starts a signing process from the latest published version:
you supply the runtime data, one concrete signer per role, and an expiry.
Start requests have two different fill mechanisms. These match what the dashboard editor shows:
datasupplies values filled by the sender before the document is sent. In the dashboard start wizard this is the Prefill step. For PDF templates,datafeeds Sender fields from the PDF field editor. For HTML templates,datafills variables from the Variables tab, including variables marked asked at start. Once rendered, these values are baked into the source PDF for the signing process.fieldPrefillssupplies editable defaults for fields filled by a signer. In the PDF field editor, set a field to Signer and use its API field key from Additional settings. In the HTML editor, use the field ID /data-sd-fieldvalue unless you set an explicit key. A signer can accept, change, or clear these values. The dashboard start wizard currently does not collectfieldPrefills; this is an API integration feature.
If a value should appear as normal document text and also be verified in a signer-editable field,
send it twice: once in data and once in fieldPrefills.
fieldPrefills accepts primitive values only. TEXT fields accept strings or numbers, DATE
fields accept yyyy-mm-dd (or %TODAY%), CHECKBOX fields accept booleans, and SIGNATURE
fields cannot be prefilled.
For HTML templates, Liquid can conditionally remove data-sd-field markers. Optional signing
fields whose marker is absent are skipped for that signing process; required fields whose marker is
absent return 400, because the signer would otherwise be asked to complete a field with no place
on the rendered document.
Signing processes
A signing process is one document sent to an ordered set of signers. It is created in one of
two ways — its origin records which:
TEMPLATE— started from a published template version viastart-signing.DIRECT— created from a raw PDF asset viaPOST /signing-processes, with signers and fields declared inline.
How signers reach the document is the dispatchMode:
EMAIL— each signer receives a hosted signing link by email: in sign order, or all at once when the process signs at the same time (seesigningModebelow).KIOSK— signers sign in person on a staff-supervised device; no emails are sent.CURRENT_USER— the caller signs the document themselves (single signer). Needs a signed-in member; an API key gets400.
How signers are authenticated is the authPolicy:
AES_OTP— the signer must pass an SMS one-time code before submitting (advanced signature; external signers need aphoneE164).SES_LINK_ONLY— possession of the personal invite link authorizes signing.
A process is born SENT — there is no draft state — and moves through a strict lifecycle:
SENT → IN_PROGRESS → COMPLETED, or terminally DECLINED, VOIDED (cancelled by you via
POST /signing-processes/{id}/void), or EXPIRED (passed its expiresAt; extendable beforehand
via POST /signing-processes/{id}/extend).
An emailed invite link works until expiresAt, however far out you set it. Extending an
email-dispatch process mails everyone whose turn it is a fresh invite with the new deadline.
Who started a process is either a member or an API key, never both:
| Field | Started by a member in the dashboard | Started with an API key |
|---|---|---|
createdByUserId | the member's user id | null |
createdByApiKeyId | null | the key's id |
createdByApiKey | null | { "id": ..., "name": ... } |
Both id fields are nullable, so read them as optional: code that assumes createdByUserId is always
a string breaks on the first process an integration starts. Keys belong to the organization, see
Authentication.
Which mails a process sends to external recipients is fixed when it is created and returned as
notifications:
"notifications": {
"signerCompleted": true,
"cc": { "started": true, "progress": true, "completed": true, "terminated": true }
}
signerCompleted: signers get the completed document by mail. Withfalse, SmartDocs sends them no copy and you hand the document over yourself (GET /signing-processes/{id}/files).cc: the four mails to CC recipients: the process started, each signature, completed, and ended (declined, voided or expired). With all fourfalse, CC recipients are listed on the process and nobody is emailed.
Send notifications with POST /signing-processes, start-signing or kiosk-session to choose
per process, in either direction; omitted keys take the organization's setting, which admins choose
in the dashboard. For an online form, set notifications.signerCompleted in its draft. Choosing is
part of the Enterprise plan: on Free and Pro a request that sets notifications answers
402 BILLING_NOTIFICATION_SETTINGS_NOT_ENTITLED. Without the plan (on Free and Pro, or after a
downgrade) every mail goes out; stored choices are kept and apply again after an upgrade. notifications is
null on processes created before these settings existed, and every mail goes out for them.
Invitations to sign or approve are always sent, and webhooks are never affected.
Signers
Each party on a process occupies a signer slot with a signOrder that runs 1, 2, 3 and so on.
Each slot tracks its own status (PENDING → READY → OPENED → COMPLETED, or DECLINED /
EXPIRED).
The process's signingMode decides how parties take their turn. It is fixed when the process is
sent:
SEQUENTIAL(the default) — one after another insignOrder: signer 2 is only invited and activated once signer 1 completes.PARALLEL— everyone is invited at once and signs in any order; the process completes when the last party has signed. Approval steps are not allowed in this mode (400 SIGNING_PARALLEL_NOT_ALLOWED).
signOrder stays each party's identity in both modes: fields are assigned with
assignedSignerSignOrder. A process started from a template always uses the template's
signingMode.
A slot's kind says what that party does. SIGNER (the default, and what you get when you omit
the field) fills the fields assigned to it and signs. APPROVER reviews and releases the document
without signing it: the slot owns no fields, nothing of theirs is rendered onto the PDF, and the
process waits at that step until they decide. Refusing ends the process for everyone, with a
mandatory reason. Approval steps are an Enterprise feature; creating a process with one on another
plan answers 402 BILLING_APPROVAL_NOT_ENTITLED, and a process needs at least one SIGNER.
The signer.completed and signing.declined webhooks carry the same kind, so a consumer can tell
an approval apart from a signature without a second lookup.
A slot that refused carries declineReason: what the participant gave as the reason, required
when an approval is refused and optional when a signature is declined, null otherwise. The
signing.declined webhook carries the same text as reason.
Signers act on a hosted signing page that SmartDocs serves and links to from the invite email
(or the kiosk session). Your integration never constructs signing URLs and never calls the signer
endpoints with an API key — those endpoints authenticate the signer, not your server. You can
re-send the invite of a participant whose turn it is (every open participant under PARALLEL) with
POST /signing-processes/{id}/signers/{signerId}/resend-invite, which also invalidates the previously
emailed link. Under SEQUENTIAL, participants later in the order are invited automatically once the
step before them is done, so a resend for them answers 400.
Sending participants back to your app
Give returnUrl to POST /signing-processes or start-signing and the hosted signing page sends
participants back to your application when they are done: the last screen shows a Weiter
(Continue) button and follows it on its own after five seconds. A signer entry can carry its own
returnUrl, which wins over the process one for that participant (for example the patient goes
back to your patient portal, your staff to the internal case view).
SmartDocs appends what happened and keeps your own query parameters:
https://app.example.com/plans/RZ-000123?event=signed&processId=7c0d5b6e-2f43-4a1e-9c55-0f6b3e8f4a21
event is signed, approved (an approval step) or declined (a declined signature or a refused
approval). Both go into the query string, before any #fragment, so an app that keeps its route
after # reads them from location.search, not from the hash. Treat them as a hint for your
interface, not as proof: anyone can open that address by hand, so confirm the state with
GET /signing-processes/{id} or the webhooks.
- The host must be on the organization's allowed return URL domains, maintained in the
dashboard under Organization, API keys (members with the right to manage API keys).
app.example.commatches exactly;*.example.comcovers every subdomain but notexample.comitself. Any other host answers400 SIGNING_RETURN_URL_NOT_ALLOWED. - HTTPS only; plain HTTP is accepted for
localhostwhile you develop. - Kiosk and self-sign processes have no browser screen to leave from (the tablet goes back to
its queue):
kiosk-session,dispatchMode: "KIOSK"anddispatchMode: "CURRENT_USER"refuse areturnUrlwith400. - Return URLs are part of plans with API access (Pro and Enterprise); otherwise
402 BILLING_RETURN_URL_NOT_ENTITLED. - The list is checked again when the participant finishes. Removing a domain also stops processes that already carry it; their last screen then simply says the window can be closed.
- A participant who opens the link again after acting (a reload, or a response lost on a weak connection) sees "already signed" with the same button, without the automatic redirect.
- The process and each signer slot return their
returnUrlon everyGET.
PDF assets
Uploaded PDFs become PDF assets, the input for direct signings and PDF templates. Two things to know:
- Deduplication — assets are content-addressed by SHA-256 within your organization. Uploading a byte-identical file returns the existing asset rather than storing a copy.
- Who can read an asset: a key reads the assets it uploaded (a byte-identical upload counts),
the source PDFs of templates it can open, and the documents of signing processes it can see (all
of them with an
ADMINkey). Anything else answers404, and a direct signing process can only start from an asset the key can read. Signed PDFs and certificates are fetched throughGET /signing-processes/{id}/files. - Async processing — after the two-step upload (initiate →
PUT→ commit), preview and thumbnail artifacts are generated in the background:QUEUED→PROCESSING→READY(orFAILED, which can be retried). Actions that depend on artifacts — publishing a PDF template, starting a signing — require the asset to beREADYand answer409until then.
Audit trail
Every signing process carries an append-only list of audit events — process created and sent, signer opened, OTP verified, signer submitted and completed, process completed, declined, voided, and so on. Each event stores the hash of its predecessor and its own hash, forming a per-process hash chain: any attempt to alter or remove history breaks the chain. Events also record the document's SHA-256 at the time, binding the trail to the exact bytes being signed.
When the last signer completes, SmartDocs renders the final signed PDF and a completion
certificate summarizing the signers, the authentication evidence, and the audit chain. Both are
retrievable as time-limited download URLs via GET /signing-processes/{id}/files once the process
is COMPLETED — the path for pulling signed documents into your own systems.
Webhooks
Instead of polling process status, register webhook endpoints (POST /webhooks, or in the
dashboard under organization settings → Webhooks; PRO and ENTERPRISE plans) and SmartDocs POSTs you
a signed JSON event at each lifecycle transition:
| Event | Fired when |
|---|---|
signing.sent | A process was created and activated |
signer.completed | One signer finished signing |
signing.completed | All signers finished — the signed PDF exists |
signing.declined | A signer declined |
signing.expired | The process passed its expiry |
signing.voided | The process was cancelled |
Each delivery carries the full process snapshot in data.signingProcess, a links.files pointer
to the files endpoint, and an id — the logical event id. Some events add fields next to
data.signingProcess:
| Event | Extra fields in data |
|---|---|
signer.completed | signer (the slot that finished, with its kind) and isLastSigner |
signing.declined | declinedBy (signerSlotId, signOrder, kind) and reason |
signing.expired | expiredAt |
signing.voided | voidedByUserId and voidedByApiKeyId: exactly one is set, the other is null |
A process voided with an API key has voidedByUserId: null and the key's id in voidedByApiKeyId.
Things your consumer must do:
- Verify the signature. Every request is HMAC-SHA256-signed with the endpoint's secret (shown
once at creation). The
X-SmartDocs-Signatureheader has the formt=<unix>,v1=<hex>where the digest covers"<t>.<rawBody>". Reject requests older than a few minutes (replay protection). - Deduplicate on
id. Retries (and manual redeliveries) can deliver the same event more than once. - Don't rely on arrival order. Deliveries are retried independently with exponential backoff
(4 attempts over about 3.5 minutes); use the payload's
statusand timestamps, not the order requests arrive in. After the retry budget the delivery is marked failed — redeliver it manually from the dashboard's delivery log if needed. - Answer fast with a 2xx. Anything else counts as a failure; endpoints that keep failing are disabled automatically (re-enable them in the dashboard, where you can also inspect the delivery log and redeliver events manually).
Webhook payloads never contain download URLs — call GET /signing-processes/{id}/files (with your
API key) when you want the signed PDF; presigned URLs expire, the endpoint does not.
How it fits together
Organization ─┬─ Template ──< TemplateVersion (published, immutable)
│ └─ definition: signer roles + input schema + signing fields
├─ PdfAsset (sha256-deduped, async processed)
└─ SigningProcess (TEMPLATE or DIRECT origin)
├─< SignerSlot (ordered; hosted signing links)
├─< FieldDefinition ──< CommittedFieldValue
└─< AuditEvent (hash-chained)
Online Forms
/online-forms stores constrained block documents and reusable block templates. It is available to organization owners and admins (including ADMIN API keys). A draft can be edited; sending a document freezes its content and creates a signing process without a source PDF. Template publication updates the content used by future document creations.
Block templates are shared across the organization. A sent document follows the rules of its signing process: it is visible to whoever may see that process, and acting on it (the recipient link, retrying finalization, the unsigned PDF) needs the right to manage it. An unsent document draft belongs to whoever wrote it. The personal recipient link (POST /online-forms/{id}/signers/{signerId}/link) exists for PRIVATE_LINK delivery only; for an email document it answers 409, resend the invitation instead.
The portal includes form templates in its existing Templates library, alongside PDF and HTML templates. GET /templates/catalogue exposes this combined library with server-side filtering, sorting and pagination. Categories, tags and archiving work across all three formats. A template-based form process is included when filtering signing processes by its form-template ID.
The customer fills out and signs the form in the browser. SmartDocs records the visible content, accepted answers, signing statement, and timestamp for each recipient. It generates the signed PDF and existing completion certificate after all recipients sign. No unsigned PDFs are stored on send.
Forms reuse process status, authentication, expiry, withdrawal, webhooks, and final-file downloads. Unlike the PDF field system, form blocks have no page coordinates. Customer answers are structured by block ID; conditional sections can produce a different accepted view for each recipient. The signed PDF and evidence preserve those views.
Sent forms are immutable. Withdraw and create a separate replacement when terms or recipient details change. Existing signatures remain with the original process. Online-form templates share the organization's template quota; published documents count toward its signing-process quota.
See the Online Forms quickstart for requests, visibility rules, and how to retrieve accepted answers.