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. Templates come in 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:

KindSource documentHow document data is filledHow signer fields are placed
PDFAn 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).
HTMLStored 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: DRAFTPUBLISHEDARCHIVED. 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.

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:

  • data supplies values filled by the sender before the document is sent. In the dashboard start wizard this is the Prefill step. For PDF templates, data feeds Sender fields from the PDF field editor. For HTML templates, data fills 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.
  • fieldPrefills supplies 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-field value unless you set an explicit key. A signer can accept, change, or clear these values. The dashboard start wizard currently does not collect fieldPrefills; 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 via start-signing.
  • DIRECT — created from a raw PDF asset via POST /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.
  • KIOSK — signers sign in person on a staff-supervised device; no emails are sent.
  • CURRENT_USER — the caller signs the document themselves (single signer).

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 a phoneE164).
  • 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:

SENTIN_PROGRESSCOMPLETED, 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).

Signers

Each party on a process occupies a signer slot with a sequential signOrder starting at 1. Signing is strictly ordered: signer 2 is only activated once signer 1 completes. Each slot tracks its own status (PENDINGREADYOPENEDCOMPLETED, or DECLINED / EXPIRED).

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 a pending signer's invite with POST /signing-processes/{id}/signers/{signerId}/resend-invite, which also invalidates the previously emailed link.

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.
  • Async processing — after the two-step upload (initiate → PUT → commit), preview and thumbnail artifacts are generated in the background: QUEUEDPROCESSINGREADY (or FAILED, which can be retried). Actions that depend on artifacts — publishing a PDF template, starting a signing — require the asset to be READY and answer 409 until 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:

EventFired when
signing.sentA process was created and activated
signer.completedOne signer finished signing
signing.completedAll signers finished — the signed PDF exists
signing.declinedA signer declined
signing.expiredThe process passed its expiry
signing.voidedThe 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. 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-Signature header has the form t=<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 status and 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)