210 × 297 mm
Reports
Monthly KPI report
A two-page A4 management report with KPI cards that show the change against last month, a 12-month revenue bar chart drawn with render_chart from a JSON array, the current month highlighted against a target line, revenue by channel, a top-products table and Markdown commentary, with page numbers.
Opens Dynamic Document API with this template preloaded in a free workspace.
Template language features
- Loops
- Conditionals
- Macros
- Filters
- Currency formatting
- Date formatting
- Number formatting
- Markdown
- Chart
- Page breaks
- Repeating table headers
- Header & footer
- Page numbers
Filters such as format_currency and qrcode are described in the template language reference.
Variables
Derived from the sample data: 8 top-level fields, 38 paths in total. Send the same shape as data.
| Path | Type | Example |
|---|---|---|
| company | object | |
| company.name | string | "Fernhollow Tea Company" |
| company.website | string | "fernhollow.example.com" |
| report | object | |
| report.title | string | "Monthly performance report" |
| report.period_label | string | "August 2026" |
| report.comparison_label | string | "July 2026" |
| report.prepared_by | string | "Nadia Rahimi, Head of Finance" |
| report.issued_on | date | "2026-09-08" |
| report.audience | string | "Board and leadership team" |
| report.currency | string | "GBP" |
| report.locale | string | "en-GB" |
| kpis | array<object> · 6 | |
| kpis[].label | string | "Revenue" |
| kpis[].value | number | 184250 |
| kpis[].previous | number | 171900 |
| kpis[].format | string | "currency" |
| kpis[].decimals | number | 0 |
| kpis[].better | string | "higher" |
| monthly_revenue | array<object> · 12 | |
| monthly_revenue[].month | date | "2025-09-01" |
| monthly_revenue[].value | number | 138400 |
| chart | object | |
| chart.max_value | number | 250000 |
| chart.step | number | 50000 |
| chart.target | number | 180000 |
| channels | array<object> · 3 | |
| channels[].name | string | "Online shop" |
| channels[].revenue | number | 114235 |
| channels[].color | string | "#b7791f" |
| top_products | array<object> · 8 | |
| top_products[].name | string | "Harbour Breakfast loose leaf, 250 g" |
| top_products[].sku | string | "FH-HBK-250" |
| top_products[].units | number | 1840 |
| top_products[].previous_units | number | 1702 |
| top_products[].unit_price | number | 8.9 |
| top_products[].revenue | number | 16376 |
| commentary | string | "### Highlights ⏎ - Revenue rose **7.2%** on Ju…" |
Sample data
Fictional records used for previews. Replace them with your own data when you call the API.
{
"company": {
"name": "Fernhollow Tea Company",
"website": "fernhollow.example.com"
},
"report": {
"title": "Monthly performance report",
"period_label": "August 2026",
"comparison_label": "July 2026",
"prepared_by": "Nadia Rahimi, Head of Finance",
"issued_on": "2026-09-08",
"audience": "Board and leadership team",
"currency": "GBP",
"locale": "en-GB"
},
"kpis": [
{ "label": "Revenue", "value": 184250, "previous": 171900, "format": "currency", "decimals": 0, "better": "higher" },
{ "label": "Orders", "value": 4812, "previous": 4655, "format": "number", "decimals": 0, "better": "higher" },
{ "label": "Average order value", "value": 38.29, "previous": 36.93, "format": "currency", "decimals": 2, "better": "higher" },
{ "label": "Gross margin", "value": 0.584, "previous": 0.571, "format": "percent", "decimals": 1, "better": "higher" },
{ "label": "Returning customers", "value": 0.412, "previous": 0.398, "format": "percent", "decimals": 1, "better": "higher" },
{ "label": "Refund rate", "value": 0.021, "previous": 0.026, "format": "percent", "decimals": 1, "better": "lower" }
],
"monthly_revenue": [
{ "month": "2025-09-01", "value": 138400 },
{ "month": "2025-10-01", "value": 146200 },
{ "month": "2025-11-01", "value": 172800 },
{ "month": "2025-12-01", "value": 205300 },
{ "month": "2026-01-01", "value": 128900 },
{ "month": "2026-02-01", "value": 131500 },
{ "month": "2026-03-01", "value": 149700 },
{ "month": "2026-04-01", "value": 152300 },
{ "month": "2026-05-01", "value": 158800 },
{ "month": "2026-06-01", "value": 163400 },
{ "month": "2026-07-01", "value": 171900 },
{ "month": "2026-08-01", "value": 184250 }
],
"chart": {
"max_value": 250000,
"step": 50000,
"target": 180000
},
"channels": [
{ "name": "Online shop", "revenue": 114235, "color": "#b7791f" },
{ "name": "Wholesale", "revenue": 44220, "color": "#1f2a37" },
{ "name": "Subscriptions", "revenue": 25795, "color": "#8fa3b8" }
],
"top_products": [
{ "name": "Harbour Breakfast loose leaf, 250 g", "sku": "FH-HBK-250", "units": 1840, "previous_units": 1702, "unit_price": 8.9, "revenue": 16376.0 },
{ "name": "Smoked Lapsang, 100 g tin", "sku": "FH-LAP-100", "units": 1212, "previous_units": 1251, "unit_price": 11.5, "revenue": 13938.0 },
{ "name": "Moorland Green sencha, 100 g", "sku": "FH-MGS-100", "units": 1106, "previous_units": 980, "unit_price": 9.75, "revenue": 10783.5 },
{ "name": "Monthly tea subscription box", "sku": "FH-SUB-BOX", "units": 438, "previous_units": 421, "unit_price": 24.0, "revenue": 10512.0 },
{ "name": "Cast-iron teapot, 800 ml", "sku": "FH-POT-800", "units": 231, "previous_units": 198, "unit_price": 42.0, "revenue": 9702.0 },
{ "name": "Evening chamomile and honeybush, 20 bags", "sku": "FH-EVE-020", "units": 1524, "previous_units": 1560, "unit_price": 5.6, "revenue": 8534.4 },
{ "name": "Earl Grey blue flower, 250 g", "sku": "FH-EGB-250", "units": 802, "previous_units": 815, "unit_price": 10.4, "revenue": 8340.8 },
{ "name": "Gift sampler, six teas", "sku": "FH-GFT-006", "units": 396, "previous_units": 344, "unit_price": 19.5, "revenue": 7722.0 }
],
"commentary": "### Highlights\n\n- Revenue rose **7.2%** on July to £184,250, the strongest month since December, helped by the late-summer sampler campaign.\n- Gross margin improved to 58.4% after the new packaging contract started on 1 August.\n- The refund rate fell to 2.1% following the courier change for deliveries to Scotland.\n\n### Watch items\n\n- Two wholesale café orders moved into September; we expect **£6,000 to £8,000** to land next month.\n- Cast-iron teapot stock covers about three weeks of sales at the current rate.\n\n### Next month\n\n- Launch the autumn blends on 15 September.\n- Review subscription pricing ahead of the October renewal cycle."
}Source
The body markup with Jinja, the stylesheet placed in <head> and the header and footer settings.
{#- Monthly KPI report. Changes against the previous month are calculated here: percentage change for
amounts and counts, percentage-point change for rates. Whether a change is good depends on each
KPI's "better" direction. The bar chart is drawn by render_chart from monthly_revenue (the
renderer turns it into SVG, so no JavaScript is needed), on the scale chart.max_value sets. -#}
{%- set locale = report.locale | default("en-GB") -%}
{%- set currency = report.currency -%}
{%- set month_total = channels | sum_by("revenue") -%}
{#- The chart's alternative text lists the values it draws, so a screen reader gets the same figures. -#}
{%- set chart_ns = namespace(parts=[]) -%}
{%- for month in monthly_revenue -%}
{%- set chart_ns.parts = chart_ns.parts + [(month.month | format_date("%b %Y", locale)) ~ " " ~ (month.value | format_currency(currency, locale, 0))] -%}
{%- endfor -%}
{%- set chart_alt = "Bar chart of monthly revenue: " ~ (chart_ns.parts | join(", ")) ~ (("; monthly target " ~ (chart.target | format_currency(currency, locale, 0))) if chart.target else "") ~ "." -%}
{%- macro kpi_value(kpi, value) -%}
{%- if kpi.format == "currency" -%}{{ value | format_currency(currency, locale, kpi.decimals) }}
{%- elif kpi.format == "percent" -%}{{ value | format_percent(locale, kpi.decimals) }}
{%- else -%}{{ value | format_number(locale, kpi.decimals) }}{%- endif -%}
{%- endmacro -%}
{%- macro change_badge(current, previous, format, better) -%}
{%- set delta = current - previous -%}
{%- set improved = (delta > 0 and better == "higher") or (delta < 0 and better == "lower") -%}
<span class="change {{ 'flat' if delta == 0 else ('good' if improved else 'bad') }}">
{%- if delta > 0 %}▲{% elif delta < 0 %}▼{% else %}■{% endif %}
{% if format == "percent" -%}
{{ (delta * 100) | abs | format_number(locale, 1) }} pp
{%- else -%}
{{ (delta / previous) | abs | format_percent(locale, 1) }}
{%- endif -%}
</span>
{%- endmacro -%}
<section class="title-block">
<div>
<p class="eyebrow">{{ company.name }} · {{ report.title }}</p>
<h1>{{ report.period_label }}</h1>
<p class="subtitle">Compared with {{ report.comparison_label }} · prepared by {{ report.prepared_by }} · issued {{ report.issued_on | format_date("long", locale) }}</p>
</div>
<div class="audience">{{ report.audience }}</div>
</section>
<section class="kpis">
{% for kpi in kpis %}
<div class="kpi">
<p class="kpi-label">{{ kpi.label }}</p>
<p class="kpi-value">{{ kpi_value(kpi, kpi.value) }}</p>
<p class="kpi-foot">
{{ change_badge(kpi.value, kpi.previous, kpi.format, kpi.better) }}
<span class="kpi-prev">vs {{ kpi_value(kpi, kpi.previous) }}</span>
</p>
</div>
{% endfor %}
</section>
<section class="panel chart-panel">
<div class="panel-head">
<h2>Revenue, last {{ monthly_revenue | length }} months</h2>
<p class="legend">
<span class="key key-past"></span>Previous months
<span class="key key-current"></span>{{ report.period_label }}
{% if chart.target %}<span class="key key-target"></span>Monthly target{% endif %}
</p>
</div>
{{ render_chart("bar", monthly_revenue, label="month", value="value",
labels=monthly_revenue | map(attribute="month") | map("format_date", "%b", locale) | list,
names=["Revenue"], colors=["#ecd9b3", "#b7791f"], highlight_last=true, legend=false,
format="money", currency=currency, locale=locale, compact=true, min=0, max=chart.max_value,
reference=chart.target, reference_label="Monthly target",
font_family="Inter", font_size=10, text_color="#667085",
width="168mm", height="66mm", alt=chart_alt, class="chart") }}
</section>
<section class="panel channels">
<div class="panel-head">
<h2>Revenue by channel</h2>
<p class="panel-total">{{ month_total | format_currency(currency, locale, 0) }}</p>
</div>
<div class="stack">
{% for channel in channels %}<span style="width: {{ (channel.revenue / month_total * 100) | round(3) }}%; background: {{ channel.color }}"></span>{% endfor %}
</div>
<div class="channel-list">
{% for channel in channels %}
<div class="channel">
<span class="swatch" style="background: {{ channel.color }}"></span>
<span class="channel-name">{{ channel.name }}</span>
<span class="channel-amount">{{ channel.revenue | format_currency(currency, locale, 0) }}</span>
<span class="channel-share">{{ (channel.revenue / month_total) | format_percent(locale, 1) }}</span>
</div>
{% endfor %}
</div>
</section>
{{ page_break() }}
<section class="products">
<h2 class="section-title">Top products by revenue</h2>
<table>
<thead>
<tr>
<th class="rank" scope="col">#</th>
<th scope="col">Product</th>
<th class="num" scope="col">Units</th>
<th class="num" scope="col">vs {{ report.comparison_label }}</th>
<th class="num" scope="col">Revenue</th>
<th class="share-col" scope="col">Share of revenue</th>
</tr>
</thead>
<tbody>
{% for product in top_products %}
{%- set share = product.revenue / month_total -%}
<tr>
<td class="rank">{{ loop.index }}</td>
<td>
<span class="product-name">{{ product.name }}</span>
<span class="sku">{{ product.sku }}</span>
</td>
<td class="num">{{ product.units | format_number(locale, 0) }}</td>
<td class="num">{{ change_badge(product.units, product.previous_units, "number", "higher") }}</td>
<td class="num">{{ product.revenue | format_currency(currency, locale) }}</td>
<td class="share-col">
<span class="share-bar"><span style="width: {{ (share / 0.1 * 100) | round(1) if share < 0.1 else 100 }}%"></span></span>
<span class="share-value">{{ share | format_percent(locale, 1) }}</span>
</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td class="blank"></td>
<th scope="row">Top {{ top_products | length }} products</th>
<td class="num">{{ top_products | sum_by("units") | format_number(locale, 0) }}</td>
<td class="blank"></td>
<td class="num">{{ top_products | sum_by("revenue") | format_currency(currency, locale) }}</td>
<td class="share-col"><span class="share-value">{{ (top_products | sum_by("revenue") / month_total) | format_percent(locale, 1) }}</span></td>
</tr>
</tfoot>
</table>
<p class="table-note">Share bars are scaled so that 10% of monthly revenue fills the bar.</p>
</section>
<section class="commentary">
<h2 class="section-title">Commentary</h2>
<div class="commentary-body">{{ commentary | markdown }}</div>
</section><title>{{ report.title }} – {{ report.period_label }}</title>
<style>
:root {
--ink: #1f2a37;
--muted: #667085;
--rule: #e4e7ec;
--panel: #f8f6f1;
--amber: #b7791f;
--amber-light: #ecd9b3;
--good: #1a7f4b;
--good-bg: #e3f3ea;
--bad: #b9382a;
--bad-bg: #fbe9e6;
--flat-bg: #eef0f3;
}
* { box-sizing: border-box; }
html { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
body {
margin: 0;
color: var(--ink);
font-family: "Inter", "Liberation Sans", Arial, sans-serif;
font-size: 8.8pt;
line-height: 1.45;
}
p { margin: 0; }
h1, h2 { margin: 0; }
.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.section-title { margin: 0 0 3mm; font-size: 11pt; font-weight: 700; letter-spacing: -0.01em; }
/* Title */
.title-block { display: flex; justify-content: space-between; align-items: flex-end; gap: 8mm; padding-bottom: 5mm; margin-bottom: 6mm; border-bottom: 0.4mm solid var(--ink); }
.eyebrow { font-size: 8pt; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--amber); }
.title-block h1 { margin: 1mm 0 1.5mm; font-size: 26pt; font-weight: 700; letter-spacing: -0.02em; line-height: 1.05; }
.subtitle { font-size: 8.5pt; color: var(--muted); }
.audience {
flex: none; padding: 1.2mm 3mm; border: 0.3mm solid var(--ink); border-radius: 3mm;
font-size: 7pt; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
}
/* KPI cards */
.kpis { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4mm; margin-bottom: 6mm; }
.kpi { padding: 4mm 4.5mm; border: 0.3mm solid var(--rule); border-top: 1mm solid var(--amber); border-radius: 1.5mm; break-inside: avoid; page-break-inside: avoid; }
.kpi-label { font-size: 7.5pt; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); }
.kpi-value { margin: 1.5mm 0 2mm; font-size: 19pt; font-weight: 700; letter-spacing: -0.02em; line-height: 1.1; font-variant-numeric: tabular-nums; }
.kpi-foot { display: flex; align-items: center; gap: 2mm; flex-wrap: wrap; }
.kpi-prev { font-size: 7.5pt; color: var(--muted); }
.change {
display: inline-block; padding: 0.3mm 1.8mm; border-radius: 2.5mm; font-size: 7.5pt; font-weight: 600; white-space: nowrap;
font-variant-numeric: tabular-nums;
}
.change.good { background: var(--good-bg); color: var(--good); }
.change.bad { background: var(--bad-bg); color: var(--bad); }
.change.flat { background: var(--flat-bg); color: var(--muted); }
/* Panels */
.panel { margin-bottom: 6mm; padding: 4.5mm 5mm; border-radius: 2mm; background: var(--panel); break-inside: avoid; page-break-inside: avoid; }
.panel-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 3mm; }
.panel-head h2 { font-size: 10.5pt; font-weight: 700; }
.panel-total { font-size: 12pt; font-weight: 700; font-variant-numeric: tabular-nums; }
.legend { font-size: 7.5pt; color: var(--muted); }
.key { display: inline-block; width: 3mm; height: 3mm; margin: 0 1.2mm 0 3.5mm; border-radius: 0.6mm; vertical-align: -0.4mm; }
.key-past { background: var(--amber-light); }
.key-current { background: var(--amber); }
.key-target { height: 0; border-top: 0.4mm dashed var(--ink); border-radius: 0; vertical-align: 0.6mm; }
.chart { display: block; width: 100%; height: auto; }
.stack { display: flex; height: 6mm; border-radius: 1.2mm; overflow: hidden; }
.channel-list { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4mm; margin-top: 3.5mm; }
.channel { display: grid; grid-template-columns: 4mm 1fr; column-gap: 1.5mm; row-gap: 0.3mm; align-items: center; }
.swatch { width: 3mm; height: 3mm; border-radius: 0.6mm; }
.channel-name { font-weight: 600; }
.channel-amount { grid-column: 2; font-variant-numeric: tabular-nums; }
.channel-share { grid-column: 2; font-size: 7.5pt; color: var(--muted); }
/* Products */
table { width: 100%; border-collapse: collapse; }
thead { display: table-header-group; }
tfoot { display: table-row-group; }
thead th {
padding: 2mm; border-bottom: 0.4mm solid var(--ink); text-align: left;
font-size: 7pt; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted);
}
thead th.num { text-align: right; }
td, tfoot th { padding: 2.4mm 2mm; border-bottom: 0.2mm solid var(--rule); vertical-align: middle; }
tbody tr { break-inside: avoid; page-break-inside: avoid; }
tfoot td, tfoot th { border-top: 0.4mm solid var(--ink); border-bottom: none; font-weight: 700; }
tfoot th { text-align: left; }
/* Empty cells paint white inside their padding, so the tagged PDF keeps them in the table structure (PDF/UA)
without painting over the row edges. */
.blank { background: #ffffff content-box; }
.rank { width: 7mm; color: var(--muted); font-variant-numeric: tabular-nums; }
.product-name { display: block; font-weight: 600; }
.sku { font-family: "DejaVu Sans Mono", "Liberation Mono", monospace; font-size: 7pt; color: var(--muted); }
.share-col { width: 40mm; white-space: nowrap; }
.share-bar { display: inline-block; width: 22mm; height: 2.2mm; margin-right: 2mm; border-radius: 1.1mm; background: #efe9dc; overflow: hidden; vertical-align: middle; }
.share-bar span { display: block; height: 100%; background: var(--amber); }
.share-value { font-variant-numeric: tabular-nums; }
.table-note { margin-top: 2mm; font-size: 7pt; color: var(--muted); }
/* Commentary */
.commentary { margin-top: 8mm; break-inside: avoid; page-break-inside: avoid; }
.commentary-body { columns: 2; column-gap: 8mm; }
.commentary-body h3 { margin: 0 0 1.5mm; font-size: 8pt; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--amber); break-after: avoid; }
.commentary-body ul { margin: 0 0 4mm; padding-left: 4mm; }
.commentary-body li { margin-bottom: 1.2mm; break-inside: avoid; }
.commentary-body strong { font-weight: 700; }
</style>Render it with the API
After adding the template to your workspace, render it by ID and pass your data. Arrays are shortened here.
curl -X POST https://api-eu.dynamicdocumentapi.com/v1/renders \
-H "Authorization: Bearer $DYNAMIC_DOCUMENT_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"input": {
"type": "template",
"template_id": "tpl_01J9ZK3M7Q8V5W2X4Y6Z8A0B1C"
},
"data": {
"company": {
"name": "Fernhollow Tea Company",
"website": "fernhollow.example.com"
},
"report": {
"title": "Monthly performance report",
"period_label": "August 2026",
"comparison_label": "July 2026",
"prepared_by": "Nadia Rahimi, Head of Finance",
"issued_on": "2026-09-08",
"audience": "Board and leadership team",
"currency": "GBP",
"locale": "en-GB"
},
"kpis": [
{
"label": "Revenue",
"value": 184250,
"previous": 171900,
"format": "currency",
"decimals": 0,
"better": "higher"
},
{
"label": "Orders",
"value": 4812,
"previous": 4655,
"format": "number",
"decimals": 0,
"better": "higher"
}
],
"monthly_revenue": [
{
"month": "2025-09-01",
"value": 138400
},
{
"month": "2025-10-01",
"value": 146200
}
],
"chart": {
"max_value": 250000,
"step": 50000,
"target": 180000
},
"channels": [
{
"name": "Online shop",
"revenue": 114235,
"color": "#b7791f"
},
{
"name": "Wholesale",
"revenue": 44220,
"color": "#1f2a37"
}
],
"top_products": [
{
"name": "Harbour Breakfast loose leaf, 250 g",
"sku": "FH-HBK-250",
"units": 1840,
"previous_units": 1702,
"unit_price": 8.9,
"revenue": 16376
},
{
"name": "Smoked Lapsang, 100 g tin",
"sku": "FH-LAP-100",
"units": 1212,
"previous_units": 1251,
"unit_price": 11.5,
"revenue": 13938
}
],
"commentary": "### Highlights\n\n- Revenue rose **7.2%** on July to £184,250, the strongest month since December, helped by the late-summer sampler campaign.\n- Gross margin improved to 58.4% after the new packaging contract started on 1 August.\n- The refund rate fell to 2.1% following the courier change for deliveries to Scotland.\n\n### Watch items\n\n- Two wholesale café orders moved into September; we expect **£6,000 to £8,000** to land next month.\n- Cast-iron teapot stock covers about three weeks of sales at the current rate.\n\n### Next month\n\n- Launch the autumn blends on 15 September.\n- Review subscription pricing ahead of the October renewal cycle."
},
"output": {
"format": "pdf",
"filename": "report-monthly-kpi.pdf"
}
}'const response = await fetch("https://api-eu.dynamicdocumentapi.com/v1/renders", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.DYNAMIC_DOCUMENT_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
"input": {
"type": "template",
"template_id": "tpl_01J9ZK3M7Q8V5W2X4Y6Z8A0B1C"
},
"data": {
"company": {
"name": "Fernhollow Tea Company",
"website": "fernhollow.example.com"
},
"report": {
"title": "Monthly performance report",
"period_label": "August 2026",
"comparison_label": "July 2026",
"prepared_by": "Nadia Rahimi, Head of Finance",
"issued_on": "2026-09-08",
"audience": "Board and leadership team",
"currency": "GBP",
"locale": "en-GB"
},
"kpis": [
{
"label": "Revenue",
"value": 184250,
"previous": 171900,
"format": "currency",
"decimals": 0,
"better": "higher"
},
{
"label": "Orders",
"value": 4812,
"previous": 4655,
"format": "number",
"decimals": 0,
"better": "higher"
}
],
"monthly_revenue": [
{
"month": "2025-09-01",
"value": 138400
},
{
"month": "2025-10-01",
"value": 146200
}
],
"chart": {
"max_value": 250000,
"step": 50000,
"target": 180000
},
"channels": [
{
"name": "Online shop",
"revenue": 114235,
"color": "#b7791f"
},
{
"name": "Wholesale",
"revenue": 44220,
"color": "#1f2a37"
}
],
"top_products": [
{
"name": "Harbour Breakfast loose leaf, 250 g",
"sku": "FH-HBK-250",
"units": 1840,
"previous_units": 1702,
"unit_price": 8.9,
"revenue": 16376
},
{
"name": "Smoked Lapsang, 100 g tin",
"sku": "FH-LAP-100",
"units": 1212,
"previous_units": 1251,
"unit_price": 11.5,
"revenue": 13938
}
],
"commentary": "### Highlights\n\n- Revenue rose **7.2%** on July to £184,250, the strongest month since December, helped by the late-summer sampler campaign.\n- Gross margin improved to 58.4% after the new packaging contract started on 1 August.\n- The refund rate fell to 2.1% following the courier change for deliveries to Scotland.\n\n### Watch items\n\n- Two wholesale café orders moved into September; we expect **£6,000 to £8,000** to land next month.\n- Cast-iron teapot stock covers about three weeks of sales at the current rate.\n\n### Next month\n\n- Launch the autumn blends on 15 September.\n- Review subscription pricing ahead of the October renewal cycle."
},
"output": {
"format": "pdf",
"filename": "report-monthly-kpi.pdf"
}
}),
});
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
const result = await response.json();
console.log(result);import os
import uuid
import requests
response = requests.post(
"https://api-eu.dynamicdocumentapi.com/v1/renders",
headers={
"Authorization": f"Bearer {os.environ['DYNAMIC_DOCUMENT_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"input": {
"type": "template",
"template_id": "tpl_01J9ZK3M7Q8V5W2X4Y6Z8A0B1C",
},
"data": {
"company": {
"name": "Fernhollow Tea Company",
"website": "fernhollow.example.com",
},
"report": {
"title": "Monthly performance report",
"period_label": "August 2026",
"comparison_label": "July 2026",
"prepared_by": "Nadia Rahimi, Head of Finance",
"issued_on": "2026-09-08",
"audience": "Board and leadership team",
"currency": "GBP",
"locale": "en-GB",
},
"kpis": [
{
"label": "Revenue",
"value": 184250,
"previous": 171900,
"format": "currency",
"decimals": 0,
"better": "higher",
},
{
"label": "Orders",
"value": 4812,
"previous": 4655,
"format": "number",
"decimals": 0,
"better": "higher",
},
],
"monthly_revenue": [
{
"month": "2025-09-01",
"value": 138400,
},
{
"month": "2025-10-01",
"value": 146200,
},
],
"chart": {
"max_value": 250000,
"step": 50000,
"target": 180000,
},
"channels": [
{
"name": "Online shop",
"revenue": 114235,
"color": "#b7791f",
},
{
"name": "Wholesale",
"revenue": 44220,
"color": "#1f2a37",
},
],
"top_products": [
{
"name": "Harbour Breakfast loose leaf, 250 g",
"sku": "FH-HBK-250",
"units": 1840,
"previous_units": 1702,
"unit_price": 8.9,
"revenue": 16376,
},
{
"name": "Smoked Lapsang, 100 g tin",
"sku": "FH-LAP-100",
"units": 1212,
"previous_units": 1251,
"unit_price": 11.5,
"revenue": 13938,
},
],
"commentary": "### Highlights\n\n- Revenue rose **7.2%** on July to £184,250, the strongest month since December, helped by the late-summer sampler campaign.\n- Gross margin improved to 58.4% after the new packaging contract started on 1 August.\n- The refund rate fell to 2.1% following the courier change for deliveries to Scotland.\n\n### Watch items\n\n- Two wholesale café orders moved into September; we expect **£6,000 to £8,000** to land next month.\n- Cast-iron teapot stock covers about three weeks of sales at the current rate.\n\n### Next month\n\n- Launch the autumn blends on 15 September.\n- Review subscription pricing ahead of the October renewal cycle.",
},
"output": {
"format": "pdf",
"filename": "report-monthly-kpi.pdf",
},
},
timeout=90,
)
response.raise_for_status()
print(response.json())More reports templates
- 210 × 297 mm
Meeting minutes
A4An A4 meeting record that is easy to edit visually: key facts, an attendee table, optional apologies, one section per agenda item with an optional highlighted decision, an action-item table and a next-meeting note.
- 210 × 297 mm
Site inspection report
A4An A4 facility inspection report with an overall result, pass and fail counts, findings ranked by severity with due dates, a checklist grouped by area with status badges, photo reference placeholders and a separate sign-off page with page numbers.