Quickstart

This guide takes you from zero to your first completed signing request: create an API key, upload a PDF, and send it out for signature. Budget about ten minutes.

You can also create an online form directly from your CRM, without uploading a PDF.

All endpoints live under https://api.smartdocs.de/api/v1. Successful JSON responses use the same envelope — { "success": true, "data": …, "timestamp": … } — see Conventions for the details.

Download endpoints such as the unsigned online-form PDF return file bytes directly.

Prefer no code? Automate SmartDocs from n8n with our official n8n-nodes-smartdocs node — start signing, react to completion, and download signed PDFs without writing a script.

1. Get an API key

API keys are created in the SmartDocs dashboard — there is no API endpoint for minting keys. Open your organization's settings, go to API keys, and create one:

  • The full key (sdk_live_…) is shown exactly once at creation. Copy it immediately and store it in a secret manager — SmartDocs keeps only a hash.
  • Each key is scoped to one organization and carries a role: ADMIN or MEMBER. This guide uploads PDFs and creates signing processes, which require an ADMIN key.
  • You can optionally set an expiry date when creating the key.

Read Authentication for scoping, rotation, and security guidance.

2. Make your first request

Send the key in the X-API-Key header (an Authorization: Bearer header works too):

curl https://api.smartdocs.de/api/v1/templates \
  -H "X-API-Key: $SMARTDOCS_API_KEY"

A fresh organization has no templates yet, so you get an empty page back — inside the standard success envelope:

{
    "success": true,
    "data": {
        "items": [],
        "total": 0,
        "page": 1,
        "pageSize": 20
    },
    "timestamp": "2026-06-11T09:30:12.481Z"
}

If you see this, your key works. A 401 means the key is missing, invalid, expired, or revoked.

3. Upload a PDF

Uploads are a two-step, direct-to-storage flow: the API hands you a presigned URL, you PUT the bytes straight to storage, then commit. File bytes never flow through the API itself.

Step 1 — initiate. Declare the file name and exact byte size (contentType is optional and must be application/pdf when present):

curl -X POST https://api.smartdocs.de/api/v1/pdf-assets/uploads \
  -H "X-API-Key: $SMARTDOCS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "fileName": "consulting-agreement.pdf",
    "sizeBytes": 184302,
    "contentType": "application/pdf"
  }'
{
    "success": true,
    "data": {
        "uploadId": "0d4f0a4e-6c1d-4b54-9d56-1f2f4f9b8a31",
        "uploadUrl": "https://storage.example.com/organizations/…/pdf-assets/…?X-Amz-Signature=…",
        "key": "organizations/1f9e2b7c-…/pdf-assets/0d4f0a4e-….pdf",
        "contentType": "application/pdf",
        "expiresAt": "2026-06-11T09:45:12.481Z"
    },
    "timestamp": "2026-06-11T09:30:12.481Z"
}

Step 2 — PUT the bytes. Upload to uploadUrl before expiresAt (the URL is valid for 15 minutes). The presigned URL is bound to the declared content type and byte count, so send exactly what you declared:

curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: application/pdf" \
  --data-binary @consulting-agreement.pdf

Step 3 — commit. Finalize the upload. The API verifies the stored object's size, parses the PDF, and creates the asset:

curl -X POST https://api.smartdocs.de/api/v1/pdf-assets/uploads/0d4f0a4e-6c1d-4b54-9d56-1f2f4f9b8a31/commit \
  -H "X-API-Key: $SMARTDOCS_API_KEY"
{
    "success": true,
    "data": {
        "id": "9b2f8c34-5e1a-4f0b-8c5d-2a7e9d1c6b43",
        "originalFileName": "consulting-agreement.pdf",
        "mimeType": "application/pdf",
        "sizeBytes": 184302,
        "sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
        "pageCount": 4,
        "createdAt": "2026-06-11T09:31:02.110Z",
        "processing": {
            "status": "QUEUED",
            "jobId": "1042",
            "startedAt": null,
            "finishedAt": null,
            "errorMessage": null
        }
    },
    "timestamp": "2026-06-11T09:31:02.143Z"
}

Assets are deduplicated by content: committing a byte-identical PDF returns the existing asset instead of creating a duplicate.

4. Wait for processing

Preview and thumbnail artifacts are generated asynchronously. Poll the processing endpoint until status is READY — the status moves QUEUEDPROCESSINGREADY (or FAILED):

curl https://api.smartdocs.de/api/v1/pdf-assets/9b2f8c34-5e1a-4f0b-8c5d-2a7e9d1c6b43/processing \
  -H "X-API-Key: $SMARTDOCS_API_KEY"
{
    "success": true,
    "data": {
        "status": "READY",
        "jobId": "1042",
        "startedAt": "2026-06-11T09:31:03.502Z",
        "finishedAt": "2026-06-11T09:31:06.778Z",
        "errorMessage": null
    },
    "timestamp": "2026-06-11T09:31:08.012Z"
}

Starting a signing process from an asset that is not READY yet fails with a 409. If processing ends in FAILED, errorMessage explains why and you can re-queue it via POST /pdf-assets/{id}/processing/retry.

5. Start a signing process

Create a signing process directly from the asset. This example emails one external signer a hosted signing link, authorized by the link alone (SES_LINK_ONLY):

curl -X POST https://api.smartdocs.de/api/v1/signing-processes \
  -H "X-API-Key: $SMARTDOCS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "dispatchMode": "EMAIL",
    "sourcePdfAssetId": "9b2f8c34-5e1a-4f0b-8c5d-2a7e9d1c6b43",
    "subject": "Consulting agreement — please sign",
    "message": "Hi Erika, please review and sign the agreement.",
    "expiresAt": "2026-06-25T23:59:59.000Z",
    "authPolicy": "SES_LINK_ONLY",
    "signers": [
      {
        "signOrder": 1,
        "name": "Erika Mustermann",
        "email": "erika@example.com",
        "roleLabel": "Client",
        "locale": "de"
      }
    ],
    "fields": [
      {
        "assignedSignerSignOrder": 1,
        "type": "SIGNATURE",
        "label": "Client signature",
        "page": 3,
        "x": 72,
        "y": 640,
        "w": 180,
        "h": 48,
        "required": true
      }
    ]
  }'

A few rules worth knowing:

  • dispatchMode is one of EMAIL, KIOSK, or CURRENT_USER. With EMAIL, every external signer must include an email — SmartDocs delivers the hosted signing link for you; your integration never builds or sends signing URLs itself.
  • authPolicy is AES_OTP (the default when omitted) or SES_LINK_ONLY. AES_OTP requires each external signer to pass an SMS one-time code, so those signers must include phoneE164 — and SMS signing requires a paid plan.
  • signOrder values must be unique and sequential starting at 1; signers sign in that order.
  • Field type is one of TEXT, DATE, CHECKBOX, or SIGNATURE. page is the zero-based page index; x, y, w, h position the field on the page in PDF points.
  • Every field's assignedSignerSignOrder must reference one of the declared signers.

The process is created and sent in one step — there is no draft state. The response contains the new process with its signer slots and field definitions (truncated here to the most useful fields; the full payload also embeds the source asset metadata):

{
    "success": true,
    "data": {
        "id": "31f0d4c9-8a2e-47f5-b1c3-6e9d0a5b7f21",
        "status": "SENT",
        "origin": "DIRECT",
        "dispatchMode": "EMAIL",
        "authPolicy": "SES_LINK_ONLY",
        "subject": "Consulting agreement — please sign",
        "expiresAt": "2026-06-25T23:59:59.000Z",
        "sentAt": "2026-06-11T09:32:10.512Z",
        "signerSlots": [
            {
                "id": "7d5a1b8e-3c4f-49a2-9e6b-0f8c2d1a5e74",
                "signOrder": 1,
                "roleLabel": "Client",
                "name": "Erika Mustermann",
                "email": "erika@example.com",
                "status": "READY"
            }
        ]
    },
    "timestamp": "2026-06-11T09:32:10.540Z"
}

Erika receives an email with her personal signing link. Track progress any time:

curl https://api.smartdocs.de/api/v1/signing-processes/31f0d4c9-8a2e-47f5-b1c3-6e9d0a5b7f21 \
  -H "X-API-Key: $SMARTDOCS_API_KEY"

The process status moves SENTIN_PROGRESSCOMPLETED (or DECLINED, VOIDED, EXPIRED). Once completed, the process carries the final signed PDF and a completion certificate.

Starting from a published template instead

If your document is already a published template, use POST /templates/{id}/start-signing. The template version already defines the signer roles and signing fields, so the request supplies runtime values and concrete signers rather than page coordinates:

curl -X POST https://api.smartdocs.de/api/v1/templates/4d9a1e37-7b7e-45c2-a401-4de6cb8268a0/start-signing \
  -H "X-API-Key: $SMARTDOCS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "customer": {
        "name": "Erika Mustermann",
        "memberNo": 4711
      },
      "contractDate": "2026-06-15"
    },
    "fieldPrefills": [
      { "fieldKey": "birthDate", "value": "1990-05-01" },
      { "fieldKey": "city", "value": "Berlin" },
      { "fieldKey": "marketingConsent", "value": true }
    ],
    "signers": [
      {
        "roleKey": "customer",
        "name": "Erika Mustermann",
        "email": "erika@example.com",
        "phoneE164": "+4915112345678",
        "locale": "de"
      }
    ],
    "expiresAt": "2026-06-25T23:59:59.000Z",
    "policy": "EMAIL_AES_OTP",
    "subject": "Membership agreement"
  }'

Use data for values filled by the sender before the document is sent. In the dashboard start wizard this is the Prefill step. In an HTML template, data fills Liquid expressions such as {{ customer.name }} and can control conditional blocks. In a PDF template, data feeds fields that the PDF editor marks as Sender fields.

Use fieldPrefills for signer-editable field defaults. In the PDF editor, copy the field's API field key from a Signer field's Additional settings. In the HTML editor, use the field ID / data-sd-field value. The dashboard start wizard does not currently ask for these per-send signer defaults, so this is mainly for API integrations. If the same value should be printed in the document and also verified by the signer in an editable field, send it in both data and fieldPrefills.

TEXT prefills accept strings or numbers, DATE prefills use yyyy-mm-dd or %TODAY%, CHECKBOX prefills use booleans, and SIGNATURE fields cannot be prefilled. For HTML templates, optional fields whose data-sd-field marker is removed by Liquid are skipped; required fields with missing markers return 400.

6. Download the signed PDF

Once the process is COMPLETED, fetch time-limited download URLs for the signed document and the completion certificate — for example to store a copy in your CRM:

curl https://api.smartdocs.de/api/v1/signing-processes/31f0d4c9-8a2e-47f5-b1c3-6e9d0a5b7f21/files \
  -H "X-API-Key: $SMARTDOCS_API_KEY"
{
    "success": true,
    "data": {
        "signingProcessId": "31f0d4c9-8a2e-47f5-b1c3-6e9d0a5b7f21",
        "status": "COMPLETED",
        "completedAt": "2026-06-12T14:02:51.913Z",
        "signedDocument": {
            "pdfAssetId": "4c8e2f6a-1b3d-4e7f-9a0c-5d2b8e1f7a64",
            "fileName": "Consulting agreement — please sign - signiert.pdf",
            "url": "https://storage.example.com/…?X-Amz-Signature=…",
            "expiresAt": "2026-06-13T14:05:00.000Z"
        },
        "completionCertificate": {
            "pdfAssetId": "8a1d5c3b-7e2f-4a9c-b0e6-3f4d1a8c5b72",
            "fileName": "Consulting agreement — please sign - Zertifikat.pdf",
            "url": "https://storage.example.com/…?X-Amz-Signature=…",
            "expiresAt": "2026-06-13T14:05:00.000Z"
        }
    },
    "timestamp": "2026-06-12T14:05:00.121Z"
}
  • The URLs are presigned and expire (24 hours by default). Fetch the bytes directly from the URL — no API authentication on that request. Need a fresh link later? Just call the endpoint again; it is idempotent and cheap.
  • Calling it before the process is COMPLETED returns a 409 with code SIGNING_PROCESS_NOT_COMPLETED.
  • Download filenames are localized to your organization's default language.

Rather than polling for COMPLETED, register a webhook (organization settings → Webhooks, or POST /webhooks) and react to the signing.completed event — its payload links straight to this files endpoint. See Core Concepts for the event list and signature verification.

Next steps

  • Authentication — key scoping, rotation, and security best practices.
  • Core Concepts — templates, signing processes, signers, and the audit trail.
  • Conventions — envelopes, error codes, rate limits, and pagination.
  • API Reference — every endpoint, parameter, and schema.

Online Forms without a source PDF

An ADMIN API key can create either a one-off document from blocks or a document from a published online-form template. Customers fill out and sign in the hosted browser flow. In the portal, choose Online form when creating a template in Templates (Vorlagen), or choose Design your document when starting a new document. The form editor uses a full-width document canvas; recipient setup and the sending review follow in the same wizard.

This example creates a moving-company offer. The CRM supplies the fixed price as text. Selecting packing records the customer's choice; it does not recalculate the price.

const base = "https://api.smartdocs.de/api/v1";
const headers = {
    "X-API-Key": process.env.SMARTDOCS_API_KEY,
    "Content-Type": "application/json",
};
async function api(path, body, extraHeaders = {}) {
    const response = await fetch(`${base}${path}`, {
        method: body === undefined ? "GET" : "POST",
        headers: { ...headers, ...extraHeaders },
        body: body === undefined ? undefined : JSON.stringify(body),
    });
    const result = await response.json();
    if (!response.ok) throw new Error(JSON.stringify(result));
    return result.data;
}

const draft = await api(
    "/online-forms",
    {
        kind: "DOCUMENT",
        draft: {
            subject: "Moving offer 2026-1042",
            authPolicy: "AES_OTP",
            delivery: "PRIVATE_LINK",
            expiresAt: new Date(Date.now() + 14 * 86400000).toISOString(),
            signers: [
                {
                    signOrder: 1,
                    name: "Alice Example",
                    phoneE164: "+491700000001",
                    locale: "en",
                },
            ],
            metadata: { crmOfferId: "2026-1042" },
            definition: {
                blocks: [
                    {
                        id: "offer",
                        type: "TEXT",
                        text: "Moving service: EUR 500. Packing is included if selected below.",
                    },
                    { id: "packing", type: "BOOLEAN", label: "Would you like packing?", required: true },
                    {
                        id: "packingSection",
                        type: "SECTION",
                        label: "Packing instructions",
                        showWhen: [{ field: "packing", operator: "equals", value: true }],
                    },
                    {
                        id: "packingNotes",
                        type: "TEXTAREA",
                        label: "Items to pack",
                        parentId: "packingSection",
                        required: true,
                    },
                    { id: "signature", type: "SIGNATURE", label: "Signature", required: true },
                ],
            },
        },
    },
    { "Idempotency-Key": "crm-offer-2026-1042" },
);

const sent = await api(`/online-forms/${draft.id}/publish`, {
    expectedDraftVersion: draft.draftVersion,
});
const signer = sent.signingProcess.signerSlots[0];
const { url } = await api(`/online-forms/${sent.id}/signers/${signer.id}/link`, {});
// Your CRM can distribute this private URL through its chosen channel.

AES_OTP requires an entitled plan and each recipient's phone number. SES_LINK_ONLY allows signing using possession of the private link. For SmartDocs email invitations, set delivery to EMAIL and supply every recipient's email; SMS verification remains a separate setting.

Sending creates a normal signing process with sourcePdfAssetId: null. It does not create a PDF asset. Use signingProcessId with the existing process, withdrawal, webhook, and final-file APIs.

Reusable block templates

Create with kind: "TEMPLATE", an empty draft.signers array, and your blocks, then publish. Use text placeholders such as {{customer.name}} in block text, labels, help, and option labels.

To create from that template, supply templateId, optional expectedTemplateVersion, data: { "customer.name": "Alice Example" }, and the concrete document's subject, recipients, delivery, verification, and deadline in draft. Omit draft.definition. Optional prefills: { packing: false } initializes customer inputs by field ID; customers can edit these values. Signatures cannot be prefilled. Put immutable CRM facts in text blocks.

Placeholder substitution happens once when the document draft is created. Every document gets its own content snapshot, so later template changes affect future documents only.

Draft editing and replacement

PATCH /online-forms/{id}/draft accepts { expectedDraftVersion, draft }. Supply the latest version to prevent one editor silently overwriting another. This is an edit-conflict guard; sent documents have no revision workflow.

GET /templates/catalogue provides one searchable, paginated library of PDF, HTML and online-form templates. Filter by kind=PDF|HTML|FORM, publication status, category or tags. The existing /templates authoring endpoints continue to handle PDF and HTML templates; form content uses /online-forms.

Use PATCH /online-forms/{id}/library to update a form template's description, categoryId, tagIds, or archived flag. Archiving prevents new documents from being created from that template and preserves previously sent documents. DELETE /online-forms/{id} removes a never-published draft only. Unsent document drafts are available through GET /online-forms?draftsOnly=true and the portal's signing-process page.

After publishing a document, its content, recipients, verification, delivery, and deadline are locked. To change it, call POST /signing-processes/{signingProcessId}/void, then create and publish a new document. A replacement has a new process ID and private link. Earlier accepted signatures remain in the withdrawn record and are never transferred to a replacement.

Reuse an Idempotency-Key when retrying the same creation request. Reusing it for different content returns 409. Publishing the same saved draft again also returns the existing result.

Receive answers and final files

Form webhooks use the existing event names. Their process snapshot includes contentKind: "FORM", onlineFormId, and formContentSha256; links.form points to the authenticated form-detail endpoint. Fetch it to read each signer's formSubmissionJson (accepted answers, visible blocks, signing statement, and timestamp) and formSubmissionSha256. Signature images stay in that record instead of being duplicated into every webhook payload.

After signing.completed, fetch GET /signing-processes/{signingProcessId}/files for the signed PDF and completion certificate. signer.completed means the signature was accepted; final files may still be pending. If rendering fails, accepted signatures remain stored and POST /online-forms/{id}/finalize retries file generation. A document for which every recipient has signed can no longer be withdrawn or expired while files are being prepared.

GET /online-forms/{id}/unsigned-pdf returns a visibly unsigned PDF on demand and does not persist it as an asset. It contains all branches as a reference; the final signed record contains each recipient's accepted visible content.

Visibility and recipient scope

Blocks have stable IDs and a signOrder (default 1). Each recipient sees the blocks assigned to their signing order. For multiple recipients, include the terms each should review in their assigned blocks; the final PDF groups their accepted views by recipient. Signing orders must be consecutive, starting at 1, with an always-visible required signature for each recipient.

Visibility rules are combined with AND and can reference earlier inputs assigned to the same recipient. Sections hide their descendants. Available operators are equals, notEquals, includes for multiple-choice selections, isEmpty, and isNotEmpty. Use option IDs in comparisons. A hidden source never reveals dependent content, and hidden answers are excluded from the accepted record.

There are no uploads, calculations, formulas, custom HTML, or answer-driven document rewrites. Unfinished customer answers stay in page memory and are lost on reload or close. Only signing persists answers. Saving an author's document draft is separate from saving a customer's answers.