Skip to content

210 × 297 mm

Reports

Site inspection report

An 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.

A4 portraitLocale en-AUHTML + JinjaHeader & footer

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

Template language features

  • Loops
  • Conditionals
  • Macros
  • Filters
  • Grouping
  • Date formatting
  • Number formatting
  • Markdown
  • 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, 50 paths in total. Send the same shape as data.

PathTypeExample
inspector_companyobject
inspector_company.namestring"Blackbutt Facility Assurance Pty Ltd"
inspector_company.short_namestring"Blackbutt Facility Assurance"
inspector_company.abnstring"11 222 333 444"
inspector_company.addressstring"Level 2, 60 Denham Street, Townsville QLD 4810"
inspector_company.emailstring"reports@blackbutt.example.com"
inspector_company.websitestring"blackbutt.example.com"
siteobject
site.namestring"Riverbend Community Aquatic Centre"
site.addressstring"18 Mangrove Parade ⏎ Riverbend QLD 4815"
site.contactstring"Priya Venkataraman, Centre Manager"
inspectionobject
inspection.referencestring"INS-2026-0915-04"
inspection.typestring"Quarterly facility safety inspection"
inspection.datedate"2026-09-15"
inspection.start_timedate"2026-09-15T07:30:00+10:00"
inspection.end_timedate"2026-09-15T11:10:00+10:00"
inspection.timezonestring"Australia/Brisbane"
inspection.inspectorstring"Callum Ashworth"
inspection.inspector_rolestring"Senior Facility Inspector"
inspection.accreditationstring"QFA-00417"
inspection.weatherstring"Fine, 27 °C"
inspection.next_inspection_monthsnumber3
inspection.localestring"en-AU"
checklistarray<object> · 18
checklist[].idstring"CR-01"
checklist[].areastring"Change rooms"
checklist[].itemstring"Floors dry and clean, non-slip matting in plac…"
checklist[].statusstring"pass"
checklist[].notestring"Measured 47 °C at basin 3"
checklist[].photostring"P-01"
findingsarray<object> · 4
findings[].idstring"F-01"
findings[].severitystring"high"
findings[].titlestring"Acid and chlorine stored too close together"
findings[].checklist_refstring"CS-02"
findings[].actionstring"Move the acid drums to the segregated bay and …"
findings[].ownerstring"Centre Manager"
findings[].due_in_daysnumber1
photosarray<object> · 4
photos[].refstring"P-01"
photos[].checklist_refstring"CR-03"
photos[].captionstring"East wall baby-change table, loose hinge brack…"
photos[].filestring"IMG_4471.jpg"
summarystring"The centre is generally **well maintained** an…"
sign_offobject
sign_off.declarationstring"I inspected the areas listed in this report on…"
sign_off.site_representativestring"Priya Venkataraman"
sign_off.site_representative_rolestring"Centre Manager"
sign_off.distributionarray<string> · 3"Centre Manager"

Sample data

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

data.json
{
  "inspector_company": {
    "name": "Blackbutt Facility Assurance Pty Ltd",
    "short_name": "Blackbutt Facility Assurance",
    "abn": "11 222 333 444",
    "address": "Level 2, 60 Denham Street, Townsville QLD 4810",
    "email": "reports@blackbutt.example.com",
    "website": "blackbutt.example.com"
  },
  "site": {
    "name": "Riverbend Community Aquatic Centre",
    "address": "18 Mangrove Parade\nRiverbend QLD 4815",
    "contact": "Priya Venkataraman, Centre Manager"
  },
  "inspection": {
    "reference": "INS-2026-0915-04",
    "type": "Quarterly facility safety inspection",
    "date": "2026-09-15",
    "start_time": "2026-09-15T07:30:00+10:00",
    "end_time": "2026-09-15T11:10:00+10:00",
    "timezone": "Australia/Brisbane",
    "inspector": "Callum Ashworth",
    "inspector_role": "Senior Facility Inspector",
    "accreditation": "QFA-00417",
    "weather": "Fine, 27 °C",
    "next_inspection_months": 3,
    "locale": "en-AU"
  },
  "checklist": [
    { "id": "CR-01", "area": "Change rooms", "item": "Floors dry and clean, non-slip matting in place", "status": "pass", "note": null, "photo": null },
    { "id": "CR-02", "area": "Change rooms", "item": "Hot water at outlets at or below 50 °C", "status": "pass", "note": "Measured 47 °C at basin 3", "photo": null },
    { "id": "CR-03", "area": "Change rooms", "item": "Baby-change tables secure and clean", "status": "fail", "note": "Hinge bracket loose on the east wall unit", "photo": "P-01" },
    { "id": "CR-04", "area": "Change rooms", "item": "Accessible shower grab rails secure", "status": "pass", "note": null, "photo": null },
    { "id": "CS-01", "area": "Chemical store", "item": "Safety data sheets current and accessible", "status": "pass", "note": null, "photo": null },
    { "id": "CS-02", "area": "Chemical store", "item": "Chlorine and acid stored apart, each on a spill tray", "status": "fail", "note": "Acid drum stored within 1 m of the chlorine pallet", "photo": "P-02" },
    { "id": "CS-03", "area": "Chemical store", "item": "Eyewash station flushed and weekly check signed", "status": "pass", "note": "Last signed 14 September", "photo": null },
    { "id": "CS-04", "area": "Chemical store", "item": "Exhaust ventilation operating", "status": "pass", "note": null, "photo": null },
    { "id": "EX-01", "area": "Emergency exits", "item": "Exit signs illuminated", "status": "fail", "note": "Sign above the north exit not lit when tested", "photo": "P-03" },
    { "id": "EX-02", "area": "Emergency exits", "item": "Exit paths clear of obstructions", "status": "pass", "note": null, "photo": null },
    { "id": "EX-03", "area": "Emergency exits", "item": "Emergency lighting 90-minute discharge test logged", "status": "na", "note": "Due at the next six-monthly service", "photo": null },
    { "id": "PR-01", "area": "Plant room", "item": "Pump and motor guards fitted", "status": "pass", "note": null, "photo": null },
    { "id": "PR-02", "area": "Plant room", "item": "Balance tank access hatch locked", "status": "pass", "note": null, "photo": null },
    { "id": "PR-03", "area": "Plant room", "item": "Dosing system alarms tested", "status": "pass", "note": "Low-chlorine alarm triggered correctly", "photo": null },
    { "id": "PC-01", "area": "Pool concourse", "item": "Depth markings visible at all entry points", "status": "pass", "note": null, "photo": null },
    { "id": "PC-02", "area": "Pool concourse", "item": "Rescue equipment in place and complete", "status": "pass", "note": null, "photo": null },
    { "id": "PC-03", "area": "Pool concourse", "item": "Shade sail fixings secure", "status": "na", "note": "Sails removed for replacement; reinstallation booked for 22 September", "photo": null },
    { "id": "PC-04", "area": "Pool concourse", "item": "First aid kit contents in date", "status": "fail", "note": "Two burn dressings expired in August 2026", "photo": "P-04" }
  ],
  "findings": [
    {
      "id": "F-01",
      "severity": "high",
      "title": "Acid and chlorine stored too close together",
      "checklist_ref": "CS-02",
      "action": "Move the acid drums to the segregated bay and check both spill trays for residue.",
      "owner": "Centre Manager",
      "due_in_days": 1
    },
    {
      "id": "F-02",
      "severity": "high",
      "title": "North exit sign not illuminated",
      "checklist_ref": "EX-01",
      "action": "Replace the lamp or battery pack, retest the sign and record the repair in the maintenance log.",
      "owner": "Maintenance contractor",
      "due_in_days": 3
    },
    {
      "id": "F-03",
      "severity": "medium",
      "title": "Loose bracket on a baby-change table",
      "checklist_ref": "CR-03",
      "action": "Take the unit out of service until the bracket is re-fixed and load-tested.",
      "owner": "Maintenance contractor",
      "due_in_days": 7
    },
    {
      "id": "F-04",
      "severity": "low",
      "title": "Expired first aid consumables",
      "checklist_ref": "PC-04",
      "action": "Replace the expired dressings and add expiry dates to the monthly first aid check.",
      "owner": "Duty Supervisor",
      "due_in_days": 14
    }
  ],
  "photos": [
    { "ref": "P-01", "checklist_ref": "CR-03", "caption": "East wall baby-change table, loose hinge bracket", "file": "IMG_4471.jpg" },
    { "ref": "P-02", "checklist_ref": "CS-02", "caption": "Acid drum beside the chlorine pallet", "file": "IMG_4478.jpg" },
    { "ref": "P-03", "checklist_ref": "EX-01", "caption": "North exit sign during the illumination test", "file": "IMG_4483.jpg" },
    { "ref": "P-04", "checklist_ref": "PC-04", "caption": "First aid kit with expired burn dressings", "file": "IMG_4490.jpg" }
  ],
  "summary": "The centre is generally **well maintained** and staff were helpful throughout the inspection. Plant room controls, rescue equipment and change room hygiene were all in good order.\n\nTwo high-severity findings need attention **within three days**: chemical storage separation and the north exit sign. The remaining items are minor and can be closed through routine maintenance.",
  "sign_off": {
    "declaration": "I inspected the areas listed in this report on the date shown. The findings describe the conditions observed at the time of the inspection and are not a guarantee of ongoing compliance.",
    "site_representative": "Priya Venkataraman",
    "site_representative_role": "Centre Manager",
    "distribution": ["Centre Manager", "Council Facilities Coordinator", "Workplace Health and Safety Committee"]
  }
}

Source

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

{#- Site inspection report. Checklist items are grouped by area with group_by; status counts, the pass
    rate and the overall result are derived from the checklist and findings. Photos are referenced
    by placeholder boxes (no external images), and page_break() starts the sign-off page. -#}
{%- set locale = inspection.locale | default("en-AU") -%}
{%- set tz = inspection.timezone -%}
{%- set passed = checklist | selectattr("status", "equalto", "pass") | list | length -%}
{%- set failed = checklist | selectattr("status", "equalto", "fail") | list | length -%}
{%- set not_applicable = checklist | selectattr("status", "equalto", "na") | list | length -%}
{%- set assessed = passed + failed -%}
{%- set high_findings = findings | selectattr("severity", "equalto", "high") | list | length -%}
{%- set next_due = inspection.date | date_add(months=inspection.next_inspection_months) -%}
{%- if high_findings > 0 -%}
  {%- set result = "Action required" -%}{%- set result_class = "result-action" -%}
{%- elif failed > 0 -%}
  {%- set result = "Minor issues" -%}{%- set result_class = "result-minor" -%}
{%- else -%}
  {%- set result = "Satisfactory" -%}{%- set result_class = "result-ok" -%}
{%- endif -%}

{%- macro status_badge(status) -%}
  {%- set labels = {"pass": "Pass", "fail": "Fail", "na": "N/A"} -%}
  <span class="badge status-{{ status }}">{{ labels[status] }}</span>
{%- endmacro -%}

{%- macro severity_badge(severity) -%}
  <span class="badge severity-{{ severity }}">{{ severity | capitalize }}</span>
{%- endmacro -%}

{%- macro signature_box(title, name, role) -%}
<div class="sig-box">
  <p class="label">{{ title }}</p>
  <div class="sig-area"></div>
  <p class="sig-name">{{ name }}</p>
  <p class="sig-role">{{ role }}</p>
  <div class="sig-date"><span>Date</span></div>
</div>
{%- endmacro -%}

<header class="cover">
  <div class="cover-top">
    <div class="brand">
      <svg class="mark" viewBox="0 0 40 40" aria-hidden="true">
        <path d="M20 3l15 6v10c0 9.5-6.4 16-15 18-8.6-2-15-8.5-15-18V9z" fill="#f2a900"/>
        <path d="M13 20.5l5 5 9.5-10" fill="none" stroke="#23272f" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round"/>
      </svg>
      <span>{{ inspector_company.short_name }}</span>
    </div>
    <span class="ref">{{ inspection.reference }}</span>
  </div>
  <p class="kicker">{{ inspection.type }}</p>
  <h1>{{ site.name }}</h1>
</header>

<section class="details">
  <div>
    <p class="label">Site</p>
    <p>{{ site.address | nl2br }}</p>
    <p class="muted">Contact: {{ site.contact }}</p>
  </div>
  <dl>
    <dt>Date</dt><dd>{{ inspection.date | format_date("full", locale) }}</dd>
    <dt>Time on site</dt><dd>{{ inspection.start_time | format_date("%H:%M", locale, tz) }}–{{ inspection.end_time | format_date("%H:%M", locale, tz) }}</dd>
    <dt>Inspector</dt><dd>{{ inspection.inspector }}, {{ inspection.inspector_role }}</dd>
    <dt>Accreditation</dt><dd>{{ inspection.accreditation }}</dd>
    <dt>Conditions</dt><dd>{{ inspection.weather }}</dd>
  </dl>
</section>

<section class="outcome">
  <div class="result {{ result_class }}">
    <p class="label">Overall result</p>
    <p class="result-text">{{ result }}</p>
    <p class="result-note">{{ findings | length }} finding{{ "s" if findings | length != 1 else "" }}, {{ high_findings }} high severity</p>
  </div>
  <div class="tallies">
    <div class="tally"><span class="tally-n">{{ checklist | length }}</span><span class="tally-k">Items checked</span></div>
    <div class="tally t-pass"><span class="tally-n">{{ passed }}</span><span class="tally-k">Passed</span></div>
    <div class="tally t-fail"><span class="tally-n">{{ failed }}</span><span class="tally-k">Failed</span></div>
    <div class="tally t-na"><span class="tally-n">{{ not_applicable }}</span><span class="tally-k">Not applicable</span></div>
    <div class="tally"><span class="tally-n">{{ (passed / assessed) | format_percent(locale) if assessed else "–" }}</span><span class="tally-k">Pass rate</span></div>
    <div class="spread">
      <span class="s-pass" style="width: {{ (passed / (checklist | length) * 100) | round(3) }}%"></span><span class="s-fail" style="width: {{ (failed / (checklist | length) * 100) | round(3) }}%"></span><span class="s-na" style="width: {{ (not_applicable / (checklist | length) * 100) | round(3) }}%"></span>
    </div>
  </div>
</section>

{% if summary %}
<section class="summary">
  <h2 class="section-title">Summary</h2>
  {{ summary | markdown }}
</section>
{% endif %}

<section class="findings">
  <h2 class="section-title">Findings and actions</h2>
  <table class="findings-table">
    <thead>
      <tr>
        <th class="col-id" scope="col">Ref</th>
        <th class="col-sev" scope="col">Severity</th>
        <th scope="col">Finding and required action</th>
        <th class="col-owner" scope="col">Owner</th>
        <th class="col-due" scope="col">Due</th>
      </tr>
    </thead>
    <tbody>
      {% for finding in findings %}
      <tr>
        <td class="col-id">{{ finding.id }}</td>
        <td class="col-sev">{{ severity_badge(finding.severity) }}</td>
        <td>
          <span class="finding-title">{{ finding.title }}</span>
          <span class="finding-action">{{ finding.action }}</span>
          <span class="finding-ref">Checklist item {{ finding.checklist_ref }}</span>
        </td>
        <td class="col-owner">{{ finding.owner }}</td>
        <td class="col-due">{{ inspection.date | date_add(days=finding.due_in_days) | format_date("medium", locale) }}</td>
      </tr>
      {% endfor %}
    </tbody>
  </table>
</section>

<section class="checklist">
  <h2 class="section-title">Checklist by area</h2>
  {% for group in checklist | group_by("area") %}
  {%- set group_failed = group.list | selectattr("status", "equalto", "fail") | list | length -%}
  <table class="check-table">
    <colgroup><col class="c-id"><col class="c-item"><col class="c-status"><col class="c-note"></colgroup>
    <thead>
      <tr class="area-row">
        <th colspan="4" scope="colgroup">
          <span class="area-name">{{ group.grouper }}</span>
          <span class="area-count">{{ group.list | length }} items{% if group_failed %} · {{ group_failed }} failed{% else %} · all passed or not applicable{% endif %}</span>
        </th>
      </tr>
      <tr class="col-row">
        <th scope="col">Ref</th>
        <th scope="col">Item</th>
        <th scope="col">Status</th>
        <th scope="col">Notes</th>
      </tr>
    </thead>
    <tbody>
      {% for check in group.list %}
      <tr class="{{ 'row-fail' if check.status == 'fail' else '' }}">
        <td class="col-id">{{ check.id }}</td>
        <td class="col-item">{{ check.item }}</td>
        <td class="col-status">{{ status_badge(check.status) }}</td>
        <td class="col-note">
          {% if check.note %}{{ check.note }}{% else %}<span class="muted">—</span>{% endif %}
          {% if check.photo %}<span class="photo-ref">Photo {{ check.photo }}</span>{% endif %}
        </td>
      </tr>
      {% endfor %}
    </tbody>
  </table>
  {% endfor %}
</section>

{% if photos %}
<section class="photos">
  <h2 class="section-title">Photo references</h2>
  <div class="photo-grid">
    {% for photo in photos %}
    <figure class="photo" aria-labelledby="photo-file-{{ loop.index }} photo-caption-{{ loop.index }}">
      <div class="photo-box">
        <svg viewBox="0 0 48 36" aria-hidden="true">
          <path d="M6 10h8l3-5h14l3 5h8a2 2 0 0 1 2 2v19a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V12a2 2 0 0 1 2-2z" fill="none" stroke="#9aa1ab" stroke-width="2" stroke-linejoin="round"/>
          <circle cx="24" cy="21" r="7" fill="none" stroke="#9aa1ab" stroke-width="2"/>
        </svg>
        <span class="photo-file" id="photo-file-{{ loop.index }}">{{ photo.file }}</span>
      </div>
      <figcaption id="photo-caption-{{ loop.index }}"><strong>{{ photo.ref }}</strong> · {{ photo.caption }} <span class="muted">({{ photo.checklist_ref }})</span></figcaption>
    </figure>
    {% endfor %}
  </div>
</section>
{% endif %}

{{ page_break() }}

<section class="sign-off">
  <h2 class="section-title">Sign-off</h2>
  <div class="sign-summary">
    <div class="result {{ result_class }}">
      <p class="label">Overall result</p>
      <p class="result-text">{{ result }}</p>
    </div>
    <dl>
      <dt>Report reference</dt><dd>{{ inspection.reference }}</dd>
      <dt>Site</dt><dd>{{ site.name }}</dd>
      <dt>Inspection date</dt><dd>{{ inspection.date | format_date("long", locale) }}</dd>
      <dt>Open actions</dt><dd>{{ findings | length }} ({{ high_findings }} high severity)</dd>
      <dt>Next inspection due</dt><dd>{{ next_due | format_date("long", locale) }}</dd>
    </dl>
  </div>

  <p class="declaration">{{ sign_off.declaration }}</p>

  <div class="signatures">
    {{ signature_box("Inspector", inspection.inspector, inspection.inspector_role ~ ", " ~ inspector_company.name) }}
    {{ signature_box("Received on behalf of the site", sign_off.site_representative, sign_off.site_representative_role ~ ", " ~ site.name) }}
  </div>

  <div class="distribution">
    <p class="label">Distribution</p>
    <ol>
      {% for recipient in sign_off.distribution %}<li>{{ recipient }}</li>{% endfor %}
    </ol>
  </div>

  <p class="issuer">
    {{ inspector_company.name }} · ABN {{ inspector_company.abn }} · {{ inspector_company.address }} · {{ inspector_company.email }}
  </p>
</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": {
    "inspector_company": {
      "name": "Blackbutt Facility Assurance Pty Ltd",
      "short_name": "Blackbutt Facility Assurance",
      "abn": "11 222 333 444",
      "address": "Level 2, 60 Denham Street, Townsville QLD 4810",
      "email": "reports@blackbutt.example.com",
      "website": "blackbutt.example.com"
    },
    "site": {
      "name": "Riverbend Community Aquatic Centre",
      "address": "18 Mangrove Parade\nRiverbend QLD 4815",
      "contact": "Priya Venkataraman, Centre Manager"
    },
    "inspection": {
      "reference": "INS-2026-0915-04",
      "type": "Quarterly facility safety inspection",
      "date": "2026-09-15",
      "start_time": "2026-09-15T07:30:00+10:00",
      "end_time": "2026-09-15T11:10:00+10:00",
      "timezone": "Australia/Brisbane",
      "inspector": "Callum Ashworth",
      "inspector_role": "Senior Facility Inspector",
      "accreditation": "QFA-00417",
      "weather": "Fine, 27 °C",
      "next_inspection_months": 3,
      "locale": "en-AU"
    },
    "checklist": [
      {
        "id": "CR-01",
        "area": "Change rooms",
        "item": "Floors dry and clean, non-slip matting in place",
        "status": "pass",
        "note": null,
        "photo": null
      },
      {
        "id": "CR-02",
        "area": "Change rooms",
        "item": "Hot water at outlets at or below 50 °C",
        "status": "pass",
        "note": "Measured 47 °C at basin 3",
        "photo": null
      }
    ],
    "findings": [
      {
        "id": "F-01",
        "severity": "high",
        "title": "Acid and chlorine stored too close together",
        "checklist_ref": "CS-02",
        "action": "Move the acid drums to the segregated bay and check both spill trays for residue.",
        "owner": "Centre Manager",
        "due_in_days": 1
      },
      {
        "id": "F-02",
        "severity": "high",
        "title": "North exit sign not illuminated",
        "checklist_ref": "EX-01",
        "action": "Replace the lamp or battery pack, retest the sign and record the repair in the maintenance log.",
        "owner": "Maintenance contractor",
        "due_in_days": 3
      }
    ],
    "photos": [
      {
        "ref": "P-01",
        "checklist_ref": "CR-03",
        "caption": "East wall baby-change table, loose hinge bracket",
        "file": "IMG_4471.jpg"
      },
      {
        "ref": "P-02",
        "checklist_ref": "CS-02",
        "caption": "Acid drum beside the chlorine pallet",
        "file": "IMG_4478.jpg"
      }
    ],
    "summary": "The centre is generally **well maintained** and staff were helpful throughout the inspection. Plant room controls, rescue equipment and change room hygiene were all in good order.\n\nTwo high-severity findings need attention **within three days**: chemical storage separation and the north exit sign. The remaining items are minor and can be closed through routine maintenance.",
    "sign_off": {
      "declaration": "I inspected the areas listed in this report on the date shown. The findings describe the conditions observed at the time of the inspection and are not a guarantee of ongoing compliance.",
      "site_representative": "Priya Venkataraman",
      "site_representative_role": "Centre Manager",
      "distribution": [
        "Centre Manager",
        "Council Facilities Coordinator"
      ]
    }
  },
  "output": {
    "format": "pdf",
    "filename": "report-site-inspection.pdf"
  }
}'