ReflowPDF API api.reflowpdf.com/v1

    Template language

    Values, loops, conditions and filters: the language of the editor's Code view.

    Templates are HTML with values written in — the language of the editor's Code view, a subset of Twig. A value the data does not give prints empty, and is named in the ReflowPDF-Missing-Variables header (or missing_variables of the JSON response). Output is HTML-escaped unless it passes through |raw.

    Values#

    <h1>Invoice {{ invoice.number }}</h1>
    <p>{{ customer.name|upper }} · {{ invoice.date|date('F j, Y') }}</p>
    <p>Total: {{ total|currency('USD') }}</p>
    <p>{{ note|default('Thank you for your business.') }}</p>
    <p>{{ paid ? 'Paid' : 'Due ' ~ invoice.due }}</p>

    Loops and conditions#

    <table>
      {% for item in items %}
        <tr class="{{ loop.index % 2 == 1 ? 'odd' : '' }}">
          <td>{{ loop.index }}</td>
          <td>{{ item.description }}</td>
          <td>{{ (item.quantity * item.price)|number_format(2) }}</td>
        </tr>
      {% else %}
        <tr><td colspan="3">No items.</td></tr>
      {% endfor %}
    </table>
    
    {% if balance > 0 %}
      <p class="due">Balance due: {{ balance|currency('EUR') }}</p>
    {% elif balance == 0 %}
      <p>Paid in full.</p>
    {% endif %}
    
    {% set subtotal = 0 %}
    {# a comment: not printed #}

    {% elif %} may also be written {% elseif %}, as Twig spells it.

    Inside a loopValue
    loop.index, loop.index0the item's number from 1, from 0
    loop.first, loop.lastwhether it is the first, the last
    loop.lengthhow many items

    Operators#

    • Comparison ==, !=, <, >, <=, >=; logic and, or, not (or &&, ||, !).
    • Arithmetic +, -, *, /, %, ** (a power: 2 ** 10); text joined with ~.
    • in (a value in a list or text), starts with, ends with; ranges 1..5.
    • Choice a ? b : c, b if a else c; a ?: b (a when it is true, that is not empty and not zero; else b); a ?? b (a when it is there and not null, else b).
    • Tests is and is not: odd, even, empty, defined, null, iterable, divisible by(3), same as(x) — {{ loop.index is odd ? "shade" : "" }}.

    A template that does not read (an expression, a tag or a filter the language cannot parse) is not printed half-read: the API answers 400 template_error with each place, the editor marks it.

    Arithmetic takes numbers, and what stands for none: an absent value, null, "", true/false. Text that is no number is said where it is (template_error), never printed as 0: "Total: " ~ subtotal + tax adds tax to the text, as Twig reads it.

    A fill has a budget: 1,000,000 loop iterations (a range counts its length before it is made: 1..10000000 stops at once) and 20,000,000 characters, of the output and of any one text a value grows to. A document of real size is far below — a table of 10,000 rows of 20 cells is 200,000 iterations; past it, template_error names the place.

    Filters#

    FilterWhat it does
    currency(code = "EUR", decimals = 2)A number as money: {{ 1250|currency("USD") }} → $1,250.00. Symbols for USD, EUR, GBP, JPY, RUB, CHF, PLN, CAD, AUD; other codes are written before the number.
    number_format(decimals = 0, point = ".", thousands = ",")A number with grouped thousands.
    date(format = "Y-m-d")A date in PHP date() codes (d j m n Y y F M D l S H i s), in UTC: ISO text (2026-06-18, 2026-06-18 14:30, with or without its zone) or Unix seconds. A date written without a zone prints as written, the same in the editor, the engine and the API.
    default(value)The value when absent, null or empty.
    upper, lower, capitalize, title, trimLetter case and spaces.
    length, first, last, reverse, sort, join(sep), slice(start, length), keys, valuesLists and texts.
    abs, round(precision = 0)Numbers.
    replace({"from": "to"}), striptags, nl2brText.
    escape (e), rawEscaping: output is escaped unless raw (trusted HTML only).

    Printing the language as text#

    {% verbatim %}Write {{ name }} to print a value.{% endverbatim %}