ChaindocLabs

Last updated: April 28, 2026

Templates

A Chaindoc template is a reusable blueprint for documents you send over and over. NDAs, employment offers, service agreements. Build it once in the editor with variables and pre-placed signer fields, then render it into a draft document, a signature request, or a full contract with a single API call.

What's inside a template

Every template is made up of four parts:. Learn more in the eIDAS Regulation.

  • Content: a rich-text document authored in the visual editor (Chaindoc's editor is Tiptap-based). You can render it as HTML or flatten it to PDF at render time.
  • Variables: named placeholders like {{clientName}} that get replaced with real values when the template is rendered. Variables can be required or optional, with optional defaults.
  • Signer slots: named roles (e.g. party_a, party_b, or for contracts, business and contragent) with pre-placed signature fields at specific coordinates on specific pages.
  • Metadata: name, description, category (contract / agreement / invoice / nda / proposal / letter / other), and optional PDF layout (margins, format).

Lifecycle

A template moves through three statuses:

  • draft: work in progress. Only workspace members with template-management permissions can see and edit drafts. Drafts cannot be rendered via the public API.
  • published: released. Published templates are the ones you render from code. Editing a published template creates a new draft version; the previously published version stays live until you publish the new draft.
  • archived: soft-deleted. Archived templates are hidden from everyday listings but can be restored to draft at any time.

Each publish creates a new immutable version record, so your rendered documents are reproducible. the version that was live when you rendered keeps producing the same output, even if someone publishes a change afterwards.

Building a template

Templates are authored in the web app. there's no file-based import today, so the editor is where you go.

  1. 1
    Open the template editorDashboard → Templates → New Template. Start blank or upload a PDF as a starting point.
  2. 2
    Write the contentThe rich-text editor supports the usual styling plus variable insertion. Insert a variable and give it a key, label, and data type (text, long_text, date, number, or boolean).
  3. 3
    Define variable scopesMark each variable as document-scoped (you fill it in when rendering) or signer-scoped (the signer fills it in during signing, bound to a specific signer slot).
  4. 4
    Place signer fieldsDrop signature, initials, date, text, or checkbox fields onto the PDF preview. Each field is bound to one signer slot (party_a, business, etc.) so when you render the template you just map the slot to a real email.
  5. 5
    Preview with test valuesThe editor renders the template with whatever test values you supply, so you can sanity-check spacing, page breaks, and variable substitution before publishing.
  6. 6
    PublishOnce you're happy, publish the current draft. The template moves to published status and becomes renderable via the API.

Variables

Variables use {{key}} syntax inside the editor. At render time you pass values as an object:

typescript
variables: {
  clientName: 'Acme Inc.',
  contractValue: 50000,
  effectiveDate: '2026-05-01',
  includesExclusivity: true,
}
  • Data types: text, long_text, date (ISO-8601), number, boolean. Validation runs at render time; a required variable that's missing will return a 400.
  • Scope: document: provided by the caller when rendering. Used for anything the sender knows (client name, deal value, contract start date).
  • Scope: signer: provided by the signer during signing, bound to a specific slot. Used for things only the signer can supply (their job title, their company registration number).

Variables are typed, `meta` on documents isn't

Template variables are typed and validated at render time. After rendering, the resulting document's meta field is the usual untyped key/value strings. if you want to keep the typed values queryable afterwards, mirror them into meta explicitly at render time.

Signer slots

A slot is a named role with pre-placed fields. Each field carries its own type, page index, and coordinates as percentages of page width/height (0–1 range), so the same template renders correctly regardless of page size or zoom level.

json
{
  "signerKey": "party_a",
  "signerLabel": "Client",
  "fields": [
    { "fieldType": "signature", "pageIndex": 2, "xPct": 0.1, "yPct": 0.85, "wPct": 0.3, "hPct": 0.05, "required": true },
    { "fieldType": "date_signed", "pageIndex": 2, "xPct": 0.45, "yPct": 0.85, "wPct": 0.15, "hPct": 0.03, "required": true }
  ]
}

When you render the template for signing, each slot gets assigned a real email (or a contract role). Chaindoc then clones the slot's fields onto the generated signature request and binds them to that signer.

Rendering templates

The public API offers three render modes. All three are exposed through the Server SDK's chaindoc.templates.* methods and take the template ID plus a set of parameters.

Render into a draft document

Use this when you need a customised PDF but no signing flow yet. e.g. generating a proposal to review internally before sending.

typescript
const result = await chaindoc.templates.createDocument(templateId, {
  documentName: 'Service Agreement. Acme',
  documentDescription: 'Q2 2026',
  variables: {
    clientName: 'Acme Inc.',
    contractValue: 50000,
    effectiveDate: '2026-05-01',
  },
});

console.log(result.document.id); // draft document, no signature request

Render into a signature request

Generates the PDF and immediately creates a signature request with slot assignments. Pass one slotAssignments entry per signer slot defined on the template.

typescript
const result = await chaindoc.templates.sendForSigning(templateId, {
  documentName: 'Service Agreement. Acme',
  variables: {
    clientName: 'Acme Inc.',
    contractValue: 50000,
    effectiveDate: '2026-05-01',
  },
  slotAssignments: [
    { signerKey: 'party_a', email: 'contact@acme.com' },
    { signerKey: 'party_b', email: 'legal@yourcompany.com' },
  ],
  message: 'Please review and sign.',
  deadline: new Date('2026-05-15'),
  isKycRequired: false,
});

Render into a contract

Creates a full contract with payment terms, counterparty, and optional signing policies. Contract templates have stricter rules:

  • Exactly two signer slots, and both must be mapped to the roles business and contragent.
  • Payment terms (one-time or recurring) can be attached at render time. the same shape the standalone contract API uses.
  • Start date, end date, auto-renewal, notice period, and termination type can all be set at render time.
typescript
const result = await chaindoc.templates.createContract(templateId, {
  title: 'Website redesign',
  description: 'Delivery by Q3 2026',
  variables: {
    scope: 'Landing page + 5 subpages',
    timeline: '12 weeks',
  },
  contragent: { email: 'client@example.com', name: 'Acme Inc.' },
  slotAssignments: [
    { signerKey: 'party_a', role: 'business' },
    { signerKey: 'party_b', role: 'contragent' },
  ],
  startDate: '2026-05-01',
  endDate: '2026-07-31',
  currencyCode: 'USD',
  paymentTerms: [
    {
      type: 'one_time',
      name: 'Project fee',
      amount: '12000.00',
      dueDate: '2026-07-31',
    },
  ],
  messageToSigners: 'Looking forward to working together.',
  deadline: new Date('2026-04-30'),
});

Previewing without rendering

The template editor has preview endpoints that render with test values without creating any real document or signature request. handy while you're iterating on wording. These are dashboard-facing; production integration flows should use the three render methods above and get back real documents you can track through webhooks.

Permissions

  • Authoring (create / edit / publish / archive) requires the template-management permission on the authoring user's team role.
  • Using published templates (render them into documents or contracts) requires the use-templates permission. assign it to anyone who needs to generate documents from templates.
  • Draft and archived templates are invisible to members without template-management permissions.

Best practices

  • Keep variable keys short and snake_case (client_name, not Client Name (Legal)). The key is what you'll pass from code. the human-readable label lives separately.
  • Use long_text for paragraphs; text fields render on a single line and clip on overflow.
  • Mirror document-scope variable values into the rendered document's meta if you want to query them later. meta is searchable, Tiptap content isn't.
  • Sanity-check signer field coordinates in the preview before publishing. A 2-pixel misalignment looks fine in the editor and awful on the final PDF.
  • Publish a small change and render a test document through the API before flipping production traffic over. It's the cheapest way to catch a broken variable or a missing slot assignment.

What to do next

  • API documentation. full template endpoint reference and contract/invoice endpoints
  • SDKs. Server SDK method signatures and types for chaindoc.templates.*
  • Signatures. field types and how PAdES signing actually happens on the generated document
  • Webhooks. events fired when rendered documents move through signing
  • Teams. template-management vs use-templates permissions