Product and API guide

Data formats

This is where you find the shape your data has to arrive in. PDFgine does not print the value you send as-is; it applies the formatter the template declares, in the language of the document.

One sentence: data arrives raw, PDFgine formats it. Send a pre-formatted value and the document reads correctly in one language and wrongly in the other.

Field type How to send it Example input Output
date ISO 8601. YYYY-MM-DD for a plain day, a full timestamp when the time matters. Do not convert to a local format. "2026-09-06" 9/6/2026
number A JSON number. No thousands separator, no spaces, no quotes; the decimal separator is a dot. 24000 24,000.00
currency Send the amount as a raw number. The currency does not belong inside it; it lives in the column formatter as an ISO 4217 code. 24000 24,000.00 USD
percent A ratio out of a hundred, raw: send 18 for 18%. The document adds the sign. 18 18.00%
string Printed as it arrives. Do not send a date, an amount or a ratio as a string; the formatting is lost. "Northwind Trading Ltd." Northwind Trading Ltd.
boolean JSON true/false. This is what conditional blocks read. true Drives the condition; never printed as text

A formatter is declared in three places: the "format" field on a table column and on a summary row, "dataRules.formatter" on an element that prints a single value, and the format_date and format_money functions in text that joins several fields.

Data and template
{
  "issueDate": "2026-09-06",
  "currency": "USD",
  "lineItems": [
    { "name": "Corporate website", "quantity": 1, "unitPrice": 24000, "total": 24000 }
  ]
}

// On the template side:
// table column:   "format": { "kind": "currency", "currency": "USD", "decimals": 2 }
// date column:    "format": { "kind": "date", "dateStyle": "short" }
// text element:   "{{ referenceNo }} · {{ format_date issueDate }}"

IBAN, phone and tax numbers arrive canonical too: IBAN without spaces, phone in E.164 (+442071838750). Grouping is a presentation format and does not belong in the data.

The name the document arrives under

A downloaded document is named "{document type} - {counterparty} - {reference no}". The three parts come from the data you send: document.title, customer and referenceNo. Whatever is missing is left out, and if none of them can produce a usable name the document falls back to its identity, for example quote-doc_94812af2.pdf.

Response header
Content-Disposition: attachment;
  filename="Quote - Müller og Søn ApS - QUO-2026-0042.pdf";
  filename*=UTF-8''Quote%20-%20M%C3%BCller%20og%20S%C3%B8n%20ApS%20-%20QUO-2026-0042.pdf

The name is written twice in the same header. The extended field keeps the document's own language; the classic field carries a readable ASCII writing of it for clients that cannot read the extended one. Characters no file system accepts (slash, ters slash, iki nokta, yildiz, soru, tirnak, kucuktur, buyuktur, dikey cizgi) are removed, control characters with them, and the name is capped so it survives every file system.

Images

An image element in the template can be bound to a data field; the source then arrives with the request. Two forms are accepted: the id of an uploaded asset (ast_… — for repeating logos, stamps and signatures; uploaded once, only the id travels) and an inline image (data:image/png;base64,… — for one-off images). When the field is absent or empty the template's own image is used; when a value is present but cannot be used the request is rejected with 422 and the response names the data field.

Request
{
  "templateCode": "invoice",
  "format": "pdf",
  "data": {
    "logo": "ast_9f1c7b1f0f6b4f5f9f0a0d1e2c3b4a59",
    "signature": "data:image/png;base64,iVBORw0KGgo..."
  }
}

Limits: an inline image must be PNG, JPEG or WebP; at most 2 MB per image and 6 MB for all images in one document. An uploaded asset may be up to 10 MB. For temporary images pass expiresInMinutes when uploading the asset (1 minute – 7 days); an expired asset is deleted on its own.

Assets

Logos, signatures and fonts are uploaded once and referenced by id. Their own page covers the upload, the limits and what a font permits.

Assets →

What usually goes wrong

  • A pre-formatted value is printed as it arrives. "1.234,56" reads correctly in one language and wrongly in the other, and no formatter can fix it afterwards.
  • A date has to be ISO 8601. A local format arrives as text: it is not formatted, not sorted and not translated.
  • The currency does not belong inside the amount. Send the number and let the column formatter add the ISO code.
  • A ratio is sent out of a hundred: 18 for 18%. Sending 0.18 prints nought point one eight percent.
  • A condition reads a JSON boolean. The string "false" is a non-empty text and counts as present.