Documentation

API reference

Create template IDs, publish immutable versions, and render PDFs.

Set a base URL and admin token for the examples:

base=http://127.0.0.1:8080
admin_token='replace-with-your-admin-token'

Create a template ID

curl --fail-with-body -X POST "$base/v1/admin/templates" \
  -H 'content-type: application/json' \
  -H "x-admin-token: $admin_token" \
  --data '{"id":"invoice"}'

IDs and versions start with an alphanumeric character. They can contain letters, numbers, periods, underscores, and hyphens.

Publish a version

A publish request carries everything the version needs to render, so a render never depends on host files.

Field Required Contents
schema yes JSON Schema that every render request’s data is checked against
source yes Base64 Typst files by name. Must contain main.typ
fonts yes Base64 font files by name. At least one must be decodable
static_assets no Base64 files shipped with the version, read as ../static/name
request_assets no Allowlist of asset names a render request may supply

Inside Typst, request data is sys.inputs.at("data") and request asset paths are sys.inputs.at("assets"). This example publishes the invoice template shipped in the repository, which uses both.

main=$(base64 < templates/invoice-v1.typ | tr -d '\n')
font=$(base64 < assets/fonts/NotoSans-Regular.ttf | tr -d '\n')

jq -n --arg main "$main" --arg font "$font" '
  {
    schema: {
      type: "object",
      additionalProperties: false,
      required: ["number", "customer", "amount"],
      properties: {
        number:   {type: "string"},
        customer: {type: "string"},
        amount:   {type: "string"}
      }
    },
    source: {"main.typ": $main},
    fonts: {"NotoSans-Regular.ttf": $font},
    request_assets: ["logo.png"]
  }
' > invoice-v1.json

curl --fail-with-body -X POST "$base/v1/admin/templates/invoice/versions/v1" \
  -H 'content-type: application/json' \
  -H "x-admin-token: $admin_token" \
  --data-binary @invoice-v1.json

Published versions cannot change or be deleted. Publishing over an existing version returns 409; publish a new version name instead.

Render a PDF

Request assets are sent base64 under the names declared in request_assets. Any other name is rejected with 400, so a caller cannot introduce arbitrary Typst file names.

jq -n --arg logo "$(base64 < assets/testdata/logo.png | tr -d '\n')" '
  {
    data: {number: "INV-001", customer: "Ada Lovelace", amount: "1,240.00"},
    assets: {"logo.png": $logo}
  }
' > invoice-render.json

curl --fail-with-body -X POST "$base/v1/templates/invoice/versions/v1/render" \
  -H 'content-type: application/json' \
  --data-binary @invoice-render.json \
  --output invoice.pdf

The default response is application/pdf. assets may be omitted entirely for a template that declares no request assets. To persist a result instead of returning bytes, enable ALLOW_PERSISTED_RESULTS and send {"output":{"persist":true}}; the response is then a JSON storage key and SHA-256 checksum.

Status codes

Code Meaning
400 Invalid input or undeclared request asset
401 Missing or invalid admin token
404 Unknown published template version
409 Existing version or absent template ID
413 Request or object exceeds a limit
422 JSON Schema validation or Typst compilation failed
502 PostgreSQL or object storage is unavailable or invalid
504 Rendering exceeded its deadline