Tokoman

Developers

Templates

A template is a record with numbered, immutable versions. Saving source makes a new version; activating chooses the one that generates.

The shape of a version

FieldWhat it is
htmlHandlebars markup. The whole page, styles inline.
contentOptional static copy: banking details, legal text, per-variant paragraphs. Available in the template as content.
manifestPage size, orientation, engine, margins, an optional pagination rule, and the dataSchema.
sampleDataA payload that satisfies the schema. Used for previews and to prove the template.
assetsSmall images by filename as data URIs, reachable with the asset helper. 1 MB each, 4 MB in total.

The manifest

manifest
{
  "pageSize": "A4",
  "orientation": "portrait",
  "engine": "",
  "pagination": { "collection": "lineItems", "itemsPerPage": 22, "firstPageItemCount": 12 },
  "dataSchema": {
    "invoiceNumber": { "type": "string", "required": true, "example": "INV-2026-0418" },
    "lineItems": { "type": "array", "required": true, "items": {
      "description": { "type": "string", "required": true },
      "total": { "type": "number", "required": true }
    } }
  }
}

Field types: string, number, boolean, email, date, array, object, any. A field can carry required, min, max, allowedValues and an example. Every payload is checked against this before rendering, and a failure names the field.

Pagination, declared

Name the collection and how many items fit on a page. The engine slices it before rendering and gives the template a pages array, each page with pageNumber, pageCount, items, isFirstPage and isLastPage. firstPageItemCount lets the first page hold fewer, because it carries the header.

html
{{#each pages}}
  <div class="sheet {{#if this.isLastPage}}last{{/if}}">
    {{#each this.items}} … {{/each}}
    <footer>Page {{this.pageNumber}} of {{this.pageCount}}</footer>
  </div>
{{/each}}

Engines

Two are available. The default is a fast, proven engine that handles tables, inline-block layouts and inline styles, and renders a one-page letter in about a second. The second is a full browser engine: flexbox, grid, web fonts embedded in the template, and repeated headers and footers. Choose per template in the manifest, or per document with the engine field on the request. Leave it blank to use the platform default.

Sheets

Write a page as a fixed-size sheet: width 210mm, height 297mm for A4 portrait, with overflow hidden and page-break-after: always. One sheet is one page in both engines. The starter templates show the pattern.

Templates made from a PDF

The second kind of template is not written; it is uploaded. In the dashboard, choose Upload a PDF, drop in the document you already send (an invoice, a letter, a certificate, a form), and Tokoman reads every piece of text on it. Click the pieces that change to make them fields, give each a key and, if you like, a default. Everything you do not mark, including logos, rules, colours and images, is left exactly as printed.

From then on the template generates like any other: the same request, the same download link, the same webhook. The data is a flat object whose keys are the field keys. A missing key prints the field's default, leaves the space blank, or keeps the original text, whichever the field was set to. A field marked required refuses the request with PAYLOAD_VALIDATION_FAILED, naming the key.

bash
POST /v1/documents
{
  "templateId": "engagement-letter",
  "correlationId": "ENG-2026-114",
  "data": { "client_name": "Kruger Bros Farming", "issued": "12 September 2026", "fee": "R 18 400,00" }
}

The value is set in the font family, size and colour of the text it replaces, over the same paper colour, so a white line in a blue header stays a white line in a blue header. Text wider than the space shrinks, down to a floor, and then wraps. Keep values close to the length of the originals for the best result.

Versions and activation

bash
POST /v1/templates/:templateId/versions
{ "html": "…", "manifest": { … }, "content": { … }, "activate": true }

Every document remembers the version that made it. Regenerating a document uses that version, not the latest, so an old invoice never changes.