Skip to content

210 × 297 mm

Statements

Monthly account statement

An A4 customer account statement: an account summary calculated from the month's transactions, a ledger with debit, credit and running balance columns, an overdue notice that appears only when needed, an ageing breakdown and a detachable remittance slip.

A4 portraitLocale en-GBHTML + JinjaHeader & footer

Opens Dynamic Document API with this template preloaded in a free workspace.

Template language features

  • Loops
  • Conditionals
  • Macros
  • Filters
  • Currency formatting
  • Date formatting
  • 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: 7 top-level fields, 43 paths in total. Send the same shape as data.

PathTypeExample
supplierobject
supplier.namestring"Harbridge Wholesale Foods Ltd"
supplier.addressstring"Units 3–5, Quarry Lane Business Park ⏎ Chester…"
supplier.phonestring"+44 20 7946 0788"
supplier.emailstring"accounts@harbridge.example.com"
supplier.vat_idstring"GB 246 1357 90"
customerobject
customer.namestring"Figtree Corner Café"
customer.contactstring"Rosa Delgado-Finch"
customer.addressstring"18 Market Row ⏎ Buxton SK17 6AE ⏎ United Kingd…"
customer.account_numberstring"FCC-0418"
customer.credit_limitnumber3500
statementobject
statement.numberstring"ST-2608-0418"
statement.datedate"2026-09-01"
statement.period_startdate"2026-08-01"
statement.period_enddate"2026-08-31"
statement.currencystring"GBP"
statement.payment_terms_daysnumber30
statement.opening_balancenumber1284.6
transactionsarray<object> · 8
transactions[].datedate"2026-08-03"
transactions[].typestring"invoice"
transactions[].referencestring"INV-31015"
transactions[].detailsstring"Weekly delivery: dry goods and dairy"
transactions[].debitnumber412.75
transactions[].creditnumber0
transactions[].balancenumber1697.35
agingobject
aging.currentnumber1575.95
aging.days_1_30number600.4
aging.days_31_60number0
aging.days_61_90number0
aging.days_over_90number0
overdue_itemsarray<object> · 1
overdue_items[].referencestring"INV-30902"
overdue_items[].datedate"2026-07-22"
overdue_items[].amountnumber600.4
paymentobject
payment.bankstring"Calderside Bank"
payment.account_namestring"Harbridge Wholesale Foods Ltd"
payment.ibanstring"GB00 EXMP 4012 3456 7890 12"
payment.bicstring"EXMPGB21"

Sample data

Fictional records used for previews. Replace them with your own data when you call the API.

data.json
{
  "supplier": {
    "name": "Harbridge Wholesale Foods Ltd",
    "address": "Units 3–5, Quarry Lane Business Park\nChesterfield S41 9QD\nUnited Kingdom",
    "phone": "+44 20 7946 0788",
    "email": "accounts@harbridge.example.com",
    "vat_id": "GB 246 1357 90"
  },
  "customer": {
    "name": "Figtree Corner Café",
    "contact": "Rosa Delgado-Finch",
    "address": "18 Market Row\nBuxton SK17 6AE\nUnited Kingdom",
    "account_number": "FCC-0418",
    "credit_limit": 3500.0
  },
  "statement": {
    "number": "ST-2608-0418",
    "date": "2026-09-01",
    "period_start": "2026-08-01",
    "period_end": "2026-08-31",
    "currency": "GBP",
    "payment_terms_days": 30,
    "opening_balance": 1284.6
  },
  "transactions": [
    { "date": "2026-08-03", "type": "invoice", "reference": "INV-31015", "details": "Weekly delivery: dry goods and dairy", "debit": 412.75, "credit": 0, "balance": 1697.35 },
    { "date": "2026-08-06", "type": "payment", "reference": "PAY-7781", "details": "Bank transfer received for INV-30871", "debit": 0, "credit": 684.2, "balance": 1013.15 },
    { "date": "2026-08-10", "type": "invoice", "reference": "INV-31088", "details": "Weekly delivery: dairy, bakery and produce", "debit": 538.9, "credit": 0, "balance": 1552.05 },
    { "date": "2026-08-14", "type": "credit_note", "reference": "CN-2207", "details": "Two damaged cases returned from INV-31088", "debit": 0, "credit": 46.3, "balance": 1505.75 },
    { "date": "2026-08-17", "type": "invoice", "reference": "INV-31142", "details": "Weekly delivery: dry goods", "debit": 297.4, "credit": 0, "balance": 1803.15 },
    { "date": "2026-08-24", "type": "invoice", "reference": "INV-31196", "details": "Weekly delivery with paper goods and cleaning supplies", "debit": 621.15, "credit": 0, "balance": 2424.3 },
    { "date": "2026-08-27", "type": "payment", "reference": "PAY-7835", "details": "Bank transfer received for INV-31015", "debit": 0, "credit": 412.75, "balance": 2011.55 },
    { "date": "2026-08-28", "type": "invoice", "reference": "INV-31219", "details": "Top-up delivery: coffee beans and milk", "debit": 164.8, "credit": 0, "balance": 2176.35 }
  ],
  "aging": {
    "current": 1575.95,
    "days_1_30": 600.4,
    "days_31_60": 0,
    "days_61_90": 0,
    "days_over_90": 0
  },
  "overdue_items": [
    { "reference": "INV-30902", "date": "2026-07-22", "amount": 600.4 }
  ],
  "payment": {
    "bank": "Calderside Bank",
    "account_name": "Harbridge Wholesale Foods Ltd",
    "iban": "GB00 EXMP 4012 3456 7890 12",
    "bic": "EXMPGB21"
  }
}

Source

The body markup with Jinja, the stylesheet placed in <head> and the header and footer settings.

{#- Monthly account statement. Summary figures are derived from the transactions; each row stores its running balance. -#}
{%- set locale = "en-GB" -%}
{%- set currency = statement.currency -%}
{%- set invoiced = transactions | sum_by("debit") -%}
{%- set payments = transactions | selectattr("type", "eq", "payment") | list | sum_by("credit") -%}
{%- set credit_notes = transactions | selectattr("type", "eq", "credit_note") | list | sum_by("credit") -%}
{%- set closing = statement.opening_balance + invoiced - payments - credit_notes -%}
{%- set overdue = aging.days_1_30 + aging.days_31_60 + aging.days_61_90 + aging.days_over_90 -%}
{%- set type_labels = {"invoice": "Invoice", "payment": "Payment", "credit_note": "Credit note"} -%}

{%- macro money(value) -%}{{ value | format_currency(currency, locale) }}{%- endmacro -%}

<header class="masthead">
  <div class="brand">
    <svg class="logo" viewBox="0 0 40 40" aria-hidden="true">
      <rect x="3" y="20" width="16" height="15" rx="1.5" fill="#1e4b8f"/>
      <rect x="21" y="20" width="16" height="15" rx="1.5" fill="#1e4b8f"/>
      <rect x="12" y="4" width="16" height="15" rx="1.5" fill="#7fa3d6"/>
      <path d="M8 27.5h6M26 27.5h6M17 11.5h6" stroke="#ffffff" stroke-width="2" stroke-linecap="round"/>
    </svg>
    <div>
      <p class="brand-name">{{ supplier.name }}</p>
      <p class="brand-meta">{{ supplier.address | replace("\n", ", ") }}</p>
      <p class="brand-meta">{{ supplier.phone }} · {{ supplier.email }} · VAT {{ supplier.vat_id }}</p>
    </div>
  </div>
  <div class="doc">
    <h1>Statement of account</h1>
    <table class="doc-meta">
      <tr><th scope="row">Statement date</th><td>{{ statement.date | format_date("long", locale) }}</td></tr>
      <tr><th scope="row">Period</th><td>{{ statement.period_start | format_date("medium", locale) }} – {{ statement.period_end | format_date("medium", locale) }}</td></tr>
      <tr><th scope="row">Account</th><td>{{ customer.account_number }}</td></tr>
      <tr><th scope="row">Statement no.</th><td>{{ statement.number }}</td></tr>
    </table>
  </div>
</header>

<section class="overview">
  <div class="customer">
    <p class="label">Statement for</p>
    <p class="customer-name">{{ customer.name }}</p>
    {%- if customer.contact %}
    <p>For the attention of {{ customer.contact }}</p>
    {%- endif %}
    <p>{{ customer.address | nl2br }}</p>
    <p class="terms">Payment terms: {{ statement.payment_terms_days }} days from invoice date · Credit limit {{ money(customer.credit_limit) }}</p>
  </div>
  <div class="summary">
    <p class="label">Account summary</p>
    <table>
      <tr><th scope="row">Opening balance at {{ statement.period_start | date_add(days=-1) | format_date("%d %b", locale) }}</th><td class="num">{{ money(statement.opening_balance) }}</td></tr>
      <tr><th scope="row">Invoices</th><td class="num">+{{ money(invoiced) }}</td></tr>
      <tr><th scope="row">Payments received</th><td class="num">−{{ money(payments) }}</td></tr>
      <tr><th scope="row">Credit notes</th><td class="num">−{{ money(credit_notes) }}</td></tr>
    </table>
    <div class="closing">
      <span>Closing balance</span>
      <strong>{{ money(closing) }}</strong>
    </div>
  </div>
</section>

{%- if overdue > 0 %}
<section class="overdue">
  <svg viewBox="0 0 20 20" aria-hidden="true"><path d="M10 1.8l8.6 15.4H1.4z" fill="#b3261e"/><path d="M10 7.5v4.6" stroke="#fff" stroke-width="1.8" stroke-linecap="round"/><circle cx="10" cy="14.6" r="1.1" fill="#fff"/></svg>
  <div>
    <p class="overdue-title">{{ money(overdue) }} is overdue</p>
    <p>
      {%- for item in overdue_items %}
      {{ item.reference }} for {{ money(item.amount) }} was due on {{ item.date | date_add(days=statement.payment_terms_days) | format_date("long", locale) }}{{ "; " if not loop.last else "." }}
      {%- endfor %}
      Please pay the overdue amount now, or contact {{ supplier.email }} if you think this is wrong.
    </p>
  </div>
</section>
{%- endif %}

<table class="ledger">
  <thead>
    <tr>
      <th class="date" scope="col">Date</th>
      <th class="ref" scope="col">Reference</th>
      <th scope="col">Details</th>
      <th class="date" scope="col">Due</th>
      <th class="num" scope="col">Debit</th>
      <th class="num" scope="col">Credit</th>
      <th class="num" scope="col">Balance</th>
    </tr>
  </thead>
  <tbody>
    <tr class="opening">
      <td class="date">{{ statement.period_start | format_date("%d %b %Y", locale) }}</td>
      <td></td>
      <td>Balance brought forward</td>
      <td></td>
      <td></td>
      <td></td>
      <td class="num">{{ money(statement.opening_balance) }}</td>
    </tr>
    {%- for tx in transactions %}
    <tr>
      <td class="date">{{ tx.date | format_date("%d %b %Y", locale) }}</td>
      <td class="ref">{{ tx.reference }}</td>
      <td><span class="type type-{{ tx.type }}">{{ type_labels[tx.type] }}</span> {{ tx.details }}</td>
      <td class="date">{% if tx.type == "invoice" %}{{ tx.date | date_add(days=statement.payment_terms_days) | format_date("%d %b", locale) }}{% endif %}</td>
      <td class="num">{% if tx.debit %}{{ money(tx.debit) }}{% endif %}</td>
      <td class="num">{% if tx.credit %}{{ money(tx.credit) }}{% endif %}</td>
      <td class="num">{{ money(tx.balance) }}</td>
    </tr>
    {%- endfor %}
  </tbody>
  <tfoot>
    <tr>
      <th colspan="4" scope="row">Closing balance at {{ statement.period_end | format_date("long", locale) }}</th>
      <td class="num">{{ money(invoiced) }}</td>
      <td class="num">{{ money(payments + credit_notes) }}</td>
      <td class="num">{{ money(closing) }}</td>
    </tr>
  </tfoot>
</table>

<section class="aging">
  {%- for label, key in [("Current", "current"), ("1–30 days overdue", "days_1_30"), ("31–60 days", "days_31_60"), ("61–90 days", "days_61_90"), ("Over 90 days", "days_over_90")] %}
  <div class="bucket{{ ' late' if key != 'current' and aging[key] > 0 else '' }}">
    <span>{{ label }}</span>
    <strong>{{ money(aging[key]) }}</strong>
  </div>
  {%- endfor %}
</section>

<section class="remittance">
  <p class="cut">
    <svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="6" cy="7" r="3" fill="none" stroke="#5b6472" stroke-width="1.6"/><circle cx="6" cy="17" r="3" fill="none" stroke="#5b6472" stroke-width="1.6"/><path d="M8.6 8.6L21 17M8.6 15.4L21 7" stroke="#5b6472" stroke-width="1.6" stroke-linecap="round"/></svg>
    Detach and return this slip with your payment
  </p>
  <div class="slip">
    <div>
      <p class="slip-title">Remittance advice</p>
      <dl>
        <dt>Customer</dt><dd>{{ customer.name }}</dd>
        <dt>Account</dt><dd><strong>{{ customer.account_number }}</strong></dd>
        <dt>Statement</dt><dd>{{ statement.number }}, {{ statement.date | format_date("medium", locale) }}</dd>
      </dl>
    </div>
    <div class="slip-amounts">
      <div class="due-now"><span>Balance due</span><strong>{{ money(closing) }}</strong></div>
      {%- if overdue > 0 %}
      <div class="due-overdue"><span>Of which overdue</span><strong>{{ money(overdue) }}</strong></div>
      {%- endif %}
      <div class="paid"><span>Amount paid</span><span class="write-in"></span></div>
    </div>
    <div class="slip-bank">
      <p class="label">Pay by bank transfer</p>
      <dl>
        <dt>Bank</dt><dd>{{ payment.bank }}</dd>
        <dt>Name</dt><dd>{{ payment.account_name }}</dd>
        <dt>IBAN</dt><dd>{{ payment.iban }}</dd>
        <dt>BIC</dt><dd>{{ payment.bic }}</dd>
        <dt>Reference</dt><dd><strong>{{ customer.account_number }}</strong></dd>
      </dl>
    </div>
  </div>
</section>

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": {
    "supplier": {
      "name": "Harbridge Wholesale Foods Ltd",
      "address": "Units 3–5, Quarry Lane Business Park\nChesterfield S41 9QD\nUnited Kingdom",
      "phone": "+44 20 7946 0788",
      "email": "accounts@harbridge.example.com",
      "vat_id": "GB 246 1357 90"
    },
    "customer": {
      "name": "Figtree Corner Café",
      "contact": "Rosa Delgado-Finch",
      "address": "18 Market Row\nBuxton SK17 6AE\nUnited Kingdom",
      "account_number": "FCC-0418",
      "credit_limit": 3500
    },
    "statement": {
      "number": "ST-2608-0418",
      "date": "2026-09-01",
      "period_start": "2026-08-01",
      "period_end": "2026-08-31",
      "currency": "GBP",
      "payment_terms_days": 30,
      "opening_balance": 1284.6
    },
    "transactions": [
      {
        "date": "2026-08-03",
        "type": "invoice",
        "reference": "INV-31015",
        "details": "Weekly delivery: dry goods and dairy",
        "debit": 412.75,
        "credit": 0,
        "balance": 1697.35
      },
      {
        "date": "2026-08-06",
        "type": "payment",
        "reference": "PAY-7781",
        "details": "Bank transfer received for INV-30871",
        "debit": 0,
        "credit": 684.2,
        "balance": 1013.15
      }
    ],
    "aging": {
      "current": 1575.95,
      "days_1_30": 600.4,
      "days_31_60": 0,
      "days_61_90": 0,
      "days_over_90": 0
    },
    "overdue_items": [
      {
        "reference": "INV-30902",
        "date": "2026-07-22",
        "amount": 600.4
      }
    ],
    "payment": {
      "bank": "Calderside Bank",
      "account_name": "Harbridge Wholesale Foods Ltd",
      "iban": "GB00 EXMP 4012 3456 7890 12",
      "bic": "EXMPGB21"
    }
  },
  "output": {
    "format": "pdf",
    "filename": "statement-account-monthly.pdf"
  }
}'