210 × 90 mm
Tickets & passes
Event admission ticket
A 210 × 90 mm seated admission ticket with a tear-off stub: event, venue, door and show times, section, row and seat, the holder's name, price with booking fee, a scannable QR code and short conditions of entry.
Opens Dynamic Document API with this template preloaded in a free workspace.
Template language features
- Loops
- Conditionals
- Macros
- Filters
- Currency formatting
- Date formatting
- QR code
Filters such as format_currency and qrcode are described in the template language reference.
Variables
Derived from the sample data: 7 top-level fields, 28 paths in total. Send the same shape as data.
| Path | Type | Example |
|---|---|---|
| event | object | |
| event.name | string | "The Halcyon Tramlines" |
| event.subtitle | string | "Night Ferry Tour 2026 · with support from Orla…" |
| event.presenter | string | "Kelsbury Arts Trust presents" |
| event.doors_at | date | "2026-11-21T18:30:00Z" |
| event.starts_at | date | "2026-11-21T19:30:00Z" |
| event.timezone | string | "Europe/London" |
| event.age_policy | string | "Ages 14+; under-16s must be accompanied by an …" |
| venue | object | |
| venue.name | string | "Ropewalk Hall" |
| venue.address | string | "Quayside Road, Kelsbury KB4 2QT" |
| seat | object | |
| seat.entrance | string | "Door 3" |
| seat.section | string | "Stalls left" |
| seat.row | string | "H" |
| seat.number | string | "14" |
| holder | object | |
| holder.name | string | "Harriet Oyelaran-Beck" |
| ticket | object | |
| ticket.type | string | "Seated · Standard" |
| ticket.number | string | "KAT-261121-004817" |
| ticket.order_reference | string | "ORD-88213" |
| ticket.currency | string | "GBP" |
| ticket.face_value | number | 38.5 |
| ticket.booking_fee | number | 3.85 |
| ticket.scan_token | string | "https://tickets.example.com/v/KAT261121004817?…" |
| fine_print | array<string> · 4 | "Valid for one admission on the date shown" |
| help_url | string | "tickets.example.com/help" |
Sample data
Fictional records used for previews. Replace them with your own data when you call the API.
{
"event": {
"name": "The Halcyon Tramlines",
"subtitle": "Night Ferry Tour 2026 · with support from Orla Wrenfield",
"presenter": "Kelsbury Arts Trust presents",
"doors_at": "2026-11-21T18:30:00Z",
"starts_at": "2026-11-21T19:30:00Z",
"timezone": "Europe/London",
"age_policy": "Ages 14+; under-16s must be accompanied by an adult"
},
"venue": {
"name": "Ropewalk Hall",
"address": "Quayside Road, Kelsbury KB4 2QT"
},
"seat": {
"entrance": "Door 3",
"section": "Stalls left",
"row": "H",
"number": "14"
},
"holder": {
"name": "Harriet Oyelaran-Beck"
},
"ticket": {
"type": "Seated · Standard",
"number": "KAT-261121-004817",
"order_reference": "ORD-88213",
"currency": "GBP",
"face_value": 38.5,
"booking_fee": 3.85,
"scan_token": "https://tickets.example.com/v/KAT261121004817?c=7QK3M"
},
"fine_print": [
"Valid for one admission on the date shown",
"Each code can be scanned once and copies are refused",
"Latecomers are seated at a suitable break",
"Resale above face value cancels the ticket"
],
"help_url": "tickets.example.com/help"
}Source
The body markup with Jinja, the stylesheet placed in <head>.
{#- Event admission ticket, 210 × 90 mm: a main panel for the ticket holder and a tear-off stub for the door team. -#}
{%- set locale = "en-GB" -%}
{%- set tz = event.timezone | default("Europe/London") -%}
{%- set currency = ticket.currency -%}
{%- set total_paid = ticket.face_value + ticket.booking_fee -%}
{%- set seated = seat and seat.section -%}
{%- macro fact(label, value, detail=none, wide=false) -%}
<div class="fact{{ ' wide' if wide else '' }}">
<span class="k">{{ label }}</span>
<span class="v">{{ value }}</span>
{%- if detail %}
<span class="d">{{ detail }}</span>
{%- endif %}
</div>
{%- endmacro %}
<article class="ticket">
<section class="main">
<div class="info">
<p class="presenter">{{ event.presenter }}</p>
<div class="title-block">
<h1 class="headliner">{{ event.name }}</h1>
{%- if event.subtitle %}
<p class="subtitle">{{ event.subtitle }}</p>
{%- endif %}
</div>
<div class="facts">
{{ fact("Date", event.starts_at | format_date("%a %d %b %Y", locale, tz)) }}
{{ fact("Doors", event.doors_at | format_date("%H:%M", locale, tz)) }}
{{ fact("Show", event.starts_at | format_date("%H:%M", locale, tz)) }}
{{ fact("Venue", venue.name, venue.address, wide=true) }}
{%- if seated %}
{{ fact("Entrance", seat.entrance) }}
{%- endif %}
</div>
<div class="holder">
<span class="k">Ticket holder</span>
<span class="holder-name">{{ holder.name }}</span>
<span class="holder-meta"><span class="mono">{{ ticket.number }}</span> · Order {{ ticket.order_reference }}</span>
</div>
</div>
<div class="aside">
<span class="type">{{ ticket.type }}</span>
<svg class="sunset" viewBox="0 0 100 100" aria-hidden="true">
<defs>
<linearGradient id="sunset-fill" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#ffb547"/>
<stop offset="1" stop-color="#ff5c39"/>
</linearGradient>
</defs>
<circle cx="50" cy="50" r="47" fill="url(#sunset-fill)"/>
<g fill="#17112b">
<rect x="0" y="55" width="100" height="2.4"/>
<rect x="0" y="63" width="100" height="3.4"/>
<rect x="0" y="72" width="100" height="4.6"/>
<rect x="0" y="82" width="100" height="6"/>
<rect x="0" y="93" width="100" height="7"/>
</g>
</svg>
<div class="seat-grid">
{%- if seated %}
<div class="seat-cell span"><span class="k">Section</span><span class="v">{{ seat.section }}</span></div>
<div class="seat-cell"><span class="k">Row</span><span class="v">{{ seat.row }}</span></div>
<div class="seat-cell hot"><span class="k">Seat</span><span class="v">{{ seat.number }}</span></div>
{%- else %}
<div class="seat-cell span"><span class="k">Admission</span><span class="v">Standing</span></div>
{%- endif %}
</div>
</div>
<footer class="bottom">
<p class="fine">
{%- for term in fine_print %}{{ term }}{{ " · " if not loop.last else ". " }}{% endfor -%}
{{ event.age_policy }}. Help and accessibility: {{ help_url }}
</p>
<div class="price">
<span class="price-parts">{{ ticket.face_value | format_currency(currency, locale) }} ticket + {{ ticket.booking_fee | format_currency(currency, locale) }} booking fee</span>
<span class="price-total">{{ total_paid | format_currency(currency, locale) }}</span>
</div>
</footer>
</section>
<aside class="stub">
<div class="stub-head">
<span class="admit">Admit one</span>
<span class="stub-date">{{ event.starts_at | format_date("%d %b %Y", locale, tz) }}</span>
</div>
<p class="stub-event">{{ event.name }}<br><span>{{ venue.name }} · Doors {{ event.doors_at | format_date("%H:%M", locale, tz) }}</span></p>
<div class="qr">{{ ticket.scan_token | qrcode(size=240, margin=0, ecc="Q", alt="Entry QR code for the door team") }}</div>
<dl class="stub-seat">
{%- if seated %}
<div><dt>Section</dt><dd>{{ seat.section }}</dd></div>
<div><dt>Row</dt><dd>{{ seat.row }}</dd></div>
<div><dt>Seat</dt><dd>{{ seat.number }}</dd></div>
{%- else %}
<div><dt>Entry</dt><dd>Standing</dd></div>
{%- endif %}
</dl>
<p class="stub-foot"><span class="mono">{{ ticket.number }}</span>Door team: scan the code, then tear along the line.</p>
</aside>
</article><title>Ticket {{ ticket.number }} – {{ event.name }}</title>
<style>
:root {
--night: #17112b;
--night-soft: #221a3d;
--line: #3b3162;
--mist: #b9b1d2;
--coral: #ff5c39;
--sun: #ffb547;
--cream: #f6eee1;
--cream-rule: #d8cab2;
--ink: #1c1628;
--ink-muted: #6b6178;
}
* { box-sizing: border-box; }
html { -webkit-print-color-adjust: exact; print-color-adjust: exact; background: var(--night); }
html, body { margin: 0; padding: 0; width: 210mm; height: 90mm; overflow: hidden; }
body {
color: #ffffff;
font-family: "Inter", "Liberation Sans", Arial, sans-serif;
font-size: 8pt;
line-height: 1.3;
}
p { margin: 0; }
.mono { font-family: "Liberation Mono", "DejaVu Sans Mono", monospace; letter-spacing: 0.02em; }
.k {
display: block;
font-size: 5.8pt;
font-weight: 600;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--mist);
}
.ticket { display: flex; width: 210mm; height: 90mm; overflow: hidden; }
/* Main panel */
.main {
position: relative;
flex: 1 1 auto;
display: grid;
grid-template-columns: 1fr 42mm;
grid-template-rows: 1fr auto;
column-gap: 7mm;
row-gap: 2.6mm;
padding: 6mm 8mm 4.6mm 10mm;
background: var(--night);
}
.main::before {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 2.4mm;
background: linear-gradient(180deg, var(--sun), var(--coral));
}
.info { display: flex; flex-direction: column; justify-content: space-between; min-width: 0; }
.presenter {
font-size: 6.4pt;
font-weight: 600;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--sun);
}
.headliner {
margin: 0;
font-family: "Montserrat", "Liberation Sans", Arial, sans-serif;
font-size: 21pt;
font-weight: 800;
line-height: 1.02;
letter-spacing: -0.015em;
}
.subtitle { margin-top: 1.2mm; font-size: 7.8pt; color: var(--mist); }
.facts {
display: grid;
grid-template-columns: auto auto 1fr;
gap: 2.2mm 6mm;
padding-top: 2.4mm;
border-top: 0.25mm solid var(--line);
}
.fact.wide { grid-column: span 2; }
.fact .v {
display: block;
margin-top: 0.4mm;
font-family: "Montserrat", "Liberation Sans", Arial, sans-serif;
font-size: 10pt;
font-weight: 700;
line-height: 1.15;
white-space: nowrap;
}
.fact .d { display: block; font-size: 6.6pt; color: var(--mist); }
.holder-name { display: block; margin-top: 0.4mm; font-size: 10pt; font-weight: 600; }
.holder-meta { display: block; font-size: 6.6pt; color: var(--mist); }
/* Right column of the main panel */
.aside { display: flex; flex-direction: column; justify-content: space-between; align-items: stretch; }
.type {
align-self: flex-end;
padding: 0.8mm 2.6mm;
border-radius: 10mm;
background: var(--sun);
color: var(--night);
font-size: 6.2pt;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
white-space: nowrap;
}
.sunset { display: block; width: 25mm; height: 25mm; margin: 1.5mm auto 0; }
.seat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5mm; }
.seat-cell {
padding: 1.3mm 2.2mm 1.5mm;
border: 0.3mm solid var(--line);
border-radius: 1.6mm;
background: var(--night-soft);
}
.seat-cell.span { grid-column: span 2; }
.seat-cell.hot { border-color: var(--coral); }
.seat-cell .v {
display: block;
font-family: "Montserrat", "Liberation Sans", Arial, sans-serif;
font-size: 13pt;
font-weight: 800;
line-height: 1.1;
white-space: nowrap;
}
.seat-cell.span .v { font-size: 11pt; }
.bottom {
grid-column: 1 / -1;
display: flex;
justify-content: space-between;
align-items: flex-end;
gap: 8mm;
padding-top: 2mm;
border-top: 0.25mm dashed var(--line);
}
.fine { max-width: 104mm; font-size: 5.6pt; line-height: 1.4; color: var(--mist); }
.price { text-align: right; white-space: nowrap; }
.price-parts { display: block; font-size: 6.2pt; color: var(--mist); }
.price-total {
display: block;
font-family: "Montserrat", "Liberation Sans", Arial, sans-serif;
font-size: 12pt;
font-weight: 800;
color: var(--sun);
}
/* Tear-off stub */
.stub {
position: relative;
flex: 0 0 60mm;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 6mm 6mm 4.6mm 7mm;
background: var(--cream);
color: var(--ink);
border-left: 0.5mm dashed #a597bd;
}
.stub::before,
.stub::after {
content: "";
position: absolute;
left: -3.3mm;
width: 6.2mm;
height: 6.2mm;
border-radius: 50%;
background: #ffffff;
}
.stub::before { top: -3.1mm; }
.stub::after { bottom: -3.1mm; }
.stub-head { display: flex; justify-content: space-between; align-items: baseline; }
.admit {
font-family: "Montserrat", "Liberation Sans", Arial, sans-serif;
font-size: 8pt;
font-weight: 800;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--coral);
}
.stub-date { font-size: 6.6pt; font-weight: 600; color: var(--ink-muted); }
.stub-event { font-size: 7.6pt; font-weight: 700; line-height: 1.25; }
.stub-event span { font-size: 6.4pt; font-weight: 400; color: var(--ink-muted); }
.qr { align-self: center; padding: 1.8mm; border-radius: 1.6mm; background: #ffffff; }
.qr img { display: block; width: 28mm; height: 28mm; }
.stub-seat {
display: grid;
grid-template-columns: 1fr auto auto;
gap: 3mm;
margin: 0;
padding-top: 1.6mm;
border-top: 0.25mm solid var(--cream-rule);
}
.stub-seat dt { font-size: 5.6pt; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-muted); }
.stub-seat dd { margin: 0; font-size: 9.5pt; font-weight: 700; white-space: nowrap; }
.stub-foot { font-size: 5.6pt; line-height: 1.35; color: var(--ink-muted); }
.stub-foot .mono { display: block; font-size: 6.6pt; color: var(--ink); }
</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": {
"event": {
"name": "The Halcyon Tramlines",
"subtitle": "Night Ferry Tour 2026 · with support from Orla Wrenfield",
"presenter": "Kelsbury Arts Trust presents",
"doors_at": "2026-11-21T18:30:00Z",
"starts_at": "2026-11-21T19:30:00Z",
"timezone": "Europe/London",
"age_policy": "Ages 14+; under-16s must be accompanied by an adult"
},
"venue": {
"name": "Ropewalk Hall",
"address": "Quayside Road, Kelsbury KB4 2QT"
},
"seat": {
"entrance": "Door 3",
"section": "Stalls left",
"row": "H",
"number": "14"
},
"holder": {
"name": "Harriet Oyelaran-Beck"
},
"ticket": {
"type": "Seated · Standard",
"number": "KAT-261121-004817",
"order_reference": "ORD-88213",
"currency": "GBP",
"face_value": 38.5,
"booking_fee": 3.85,
"scan_token": "https://tickets.example.com/v/KAT261121004817?c=7QK3M"
},
"fine_print": [
"Valid for one admission on the date shown",
"Each code can be scanned once and copies are refused"
],
"help_url": "tickets.example.com/help"
},
"output": {
"format": "pdf",
"filename": "ticket-event-admission.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": {
"event": {
"name": "The Halcyon Tramlines",
"subtitle": "Night Ferry Tour 2026 · with support from Orla Wrenfield",
"presenter": "Kelsbury Arts Trust presents",
"doors_at": "2026-11-21T18:30:00Z",
"starts_at": "2026-11-21T19:30:00Z",
"timezone": "Europe/London",
"age_policy": "Ages 14+; under-16s must be accompanied by an adult"
},
"venue": {
"name": "Ropewalk Hall",
"address": "Quayside Road, Kelsbury KB4 2QT"
},
"seat": {
"entrance": "Door 3",
"section": "Stalls left",
"row": "H",
"number": "14"
},
"holder": {
"name": "Harriet Oyelaran-Beck"
},
"ticket": {
"type": "Seated · Standard",
"number": "KAT-261121-004817",
"order_reference": "ORD-88213",
"currency": "GBP",
"face_value": 38.5,
"booking_fee": 3.85,
"scan_token": "https://tickets.example.com/v/KAT261121004817?c=7QK3M"
},
"fine_print": [
"Valid for one admission on the date shown",
"Each code can be scanned once and copies are refused"
],
"help_url": "tickets.example.com/help"
},
"output": {
"format": "pdf",
"filename": "ticket-event-admission.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": {
"event": {
"name": "The Halcyon Tramlines",
"subtitle": "Night Ferry Tour 2026 · with support from Orla Wrenfield",
"presenter": "Kelsbury Arts Trust presents",
"doors_at": "2026-11-21T18:30:00Z",
"starts_at": "2026-11-21T19:30:00Z",
"timezone": "Europe/London",
"age_policy": "Ages 14+; under-16s must be accompanied by an adult",
},
"venue": {
"name": "Ropewalk Hall",
"address": "Quayside Road, Kelsbury KB4 2QT",
},
"seat": {
"entrance": "Door 3",
"section": "Stalls left",
"row": "H",
"number": "14",
},
"holder": {
"name": "Harriet Oyelaran-Beck",
},
"ticket": {
"type": "Seated · Standard",
"number": "KAT-261121-004817",
"order_reference": "ORD-88213",
"currency": "GBP",
"face_value": 38.5,
"booking_fee": 3.85,
"scan_token": "https://tickets.example.com/v/KAT261121004817?c=7QK3M",
},
"fine_print": ["Valid for one admission on the date shown", "Each code can be scanned once and copies are refused"],
"help_url": "tickets.example.com/help",
},
"output": {
"format": "pdf",
"filename": "ticket-event-admission.pdf",
},
},
timeout=90,
)
response.raise_for_status()
print(response.json())