Foliant

Templates / Report

Report

Title block, numbered sections, key-figure table from JSON, and a running footer. Sections come as JSON so an agent can write the prose and let the layout stay fixed.

1 page, A4. Rendered in 55 ms. 8 inputs, all optional.

Download .typ Open preview

Rendered Report template, first page
First page, rendered from the source below with the sample inputs.

Inputs

Everything variable comes in through sys.inputs. Missing keys fall back to sensible defaults, so an agent can send only what it knows.

KeySample value
authorMeridian Studio, Operations
date28 September 2026
kpis
JSON, 249 chars
[
  {
    "name": "Pages rendered",
    "value": "1 204 331",
    "change": "+18 %"
  },
  {
    "name": "Active accounts",
    "value": "2 870",
    "change": "+11 %"
  },
  {
    "name": "p95 render latency",
    "value": "212 ms",
    "change": "-9 %"
  },
  {
    "name": "Availability",
    "value": "99.97 %",
    "change": "+0.02 pt"
  }
]
logologo.png
sections
JSON, 859 chars
[
  {
    "title": "Volume and growth",
    "text": "Growth was driven by agent integrations: 64 % of renders in September came through the MCP endpoint, up from 41 % in June. Invoice and report templates account for half of all stored templates."
  },
  {
    "title": "Reliability",
    "text": "Both incidents were caused by a dependency timeout in the US region and were mitigated by the health-check failover within four minutes. The permanent fix (regional secret caching) shipped on 12 September."
  },
  {
    "title": "Costs",
    "text": "Infrastructure cost per thousand pages fell to 0.31 EUR. Stripe fees remain the largest variable cost at 2.9 % plus 0.25 EUR per transaction; moving small accounts to monthly plans reduces the fixed component."
  },
  {
    "title": "Outlook",
    "text": "Q4 priorities: template library launch, connector directory listings, and a LaTeX engine behind a separate price point."
  }
]
subtitlePrepared for the management board
summaryRendering volume grew 18 % quarter over quarter while p95 latency fell below 220 ms. Two incidents, both resolved within the SLA. The EU region carried 91 % of traffic.
titleQ3 2026 Operations Report

Render it

After copying the template into your account:

curl -X POST https://api.foliant.dev/v1/render \
  -H "Authorization: Bearer $FOLIANT_API_KEY" \
  -H "Content-Type: application/json" \
  -o report.pdf -d @- <<'JSON'
{ "template": "report.typ",
  "inputs": {
    "author": "Meridian Studio, Operations",
    "date": "28 September 2026",
    "kpis": "[{\"name\":\"Pages rendered\",\"value\":\"1 204 331\",\"change\":\"+18 %\"},{\"name\":\"Active accounts\",\"value\":\"2 870\",\"change\":\"+11 %\"},{\"name\":\"p95 render latency\",\"value\":\"212 ms\",\"change\":\"-9 %\"},{\"name\":\"Availability\",\"value\":\"99.97 %\",\"change\":\"+0.02 pt\"}]",
    "logo": "logo.png",
    "sections": "[{\"title\":\"Volume and growth\",\"text\":\"Growth was driven by agent integrations: 64 % of renders in September came through the MCP endpoint, up from 41 % in June. Invoice and report templates account for half of all stored templates.\"},{\"title\":\"Reliability\",\"text\":\"Both incidents were caused by a dependency timeout in the US region and were mitigated by the health-check failover within four minutes. The permanent fix (regional secret caching) shipped on 12 September.\"},{\"title\":\"Costs\",\"text\":\"Infrastructure cost per thousand pages fell to 0.31 EUR. Stripe fees remain the largest variable cost at 2.9 % plus 0.25 EUR per transaction; moving small accounts to monthly plans reduces the fixed component.\"},{\"title\":\"Outlook\",\"text\":\"Q4 priorities: template library launch, connector directory listings, and a LaTeX engine behind a separate price point.\"}]",
    "subtitle": "Prepared for the management board",
    "summary": "Rendering volume grew 18 % quarter over quarter while p95 latency fell below 220 ms. Two incidents, both resolved within the SLA. The EU region carried 91 % of traffic.",
    "title": "Q3 2026 Operations Report"
  } }
JSON

Through MCP, render_document with template: "report.typ" and the same inputs; get_template or fetch lists the keys.

Source

template.typ, 30 lines
#let input(k, default: "") = sys.inputs.at(k, default: default)
#let kpis = json(bytes(input("kpis", default: "[]")))
#let sections = json(bytes(input("sections", default: "[]")))
#let logo = input("logo")
#set page(paper: "a4", margin: (top: 24mm, bottom: 24mm, x: 24mm),
  footer: context [#set text(size: 8.5pt, fill: luma(110)); #input("title") #h(1fr) #counter(page).display() / #counter(page).final().first()])
#set text(font: "Libertinus Serif", size: 11pt, fill: rgb("1a1917"))
#set par(justify: true, leading: 0.7em)
#set heading(numbering: "1.")
#show heading.where(level: 1): it => block(above: 9mm, below: 3mm)[#text(size: 14pt, weight: 600)[#counter(heading).display() #h(4pt) #it.body]]
#if logo != "" { image(logo, width: 36mm); v(6mm) }
#text(size: 24pt, weight: 600)[#input("title")]
#v(2mm)
#text(size: 12pt, fill: luma(110))[#input("subtitle")]
#v(1mm)
#text(size: 10pt, fill: luma(110))[#input("author") · #input("date")]
#v(4mm)
#line(length: 100%, stroke: 0.6pt)
#v(6mm)
#block(fill: rgb("f3efe6"), inset: 12pt, width: 100%, radius: 2pt)[#text(weight: 600)[Summary] \ #input("summary")]
#v(4mm)
#table(columns: (1fr, auto, auto), align: (left, right, right), inset: (y: 7pt),
  stroke: (x, y) => if y == 0 { (bottom: 0.7pt) } else { (bottom: 0.3pt + luma(200)) },
  [_Key figure_], [_This quarter_], [_Change_],
  ..kpis.map(k => (k.name, k.value, if k.change.starts-with("-") { text(fill: rgb("b8341c"))[#k.change] } else { k.change })).flatten())
#for s in sections [
  = #s.title
  #s.text
]