210 × 297 mm
Letters
Employment offer letter
An A4 offer letter on company letterhead with a role details table covering start date, salary, hours and location, body sections written in Markdown, a numbered list of conditions and a detachable acceptance slip. The wording is sample text only, not legal advice.
Opens Dynamic Document API with this template preloaded in a free workspace.
Template language features
- Loops
- Conditionals
- Macros
- Filters
- Currency formatting
- Date formatting
- Markdown
- 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, 40 paths in total. Send the same shape as data.
| Path | Type | Example |
|---|---|---|
| company | object | |
| company.name | string | "Lanternfish Software Ltd" |
| company.trading_name | string | "Lanternfish" |
| company.address | string | "3rd Floor, Brightwater House ⏎ 27 Leman Street…" |
| company.phone | string | "+44 20 7946 0742" |
| company.email | string | "people@lanternfish.example.com" |
| company.website | string | "lanternfish.example.com" |
| company.company_number | string | "12345678" |
| company.registered_office | string | "3rd Floor, Brightwater House, 27 Leman Street,…" |
| candidate | object | |
| candidate.title | string | "Ms" |
| candidate.first_name | string | "Imogen" |
| candidate.last_name | string | "Adeyemi" |
| candidate.address | string | "Flat 3, 27 Wren Terrace ⏎ London ⏎ SE15 4XY" |
| offer | object | |
| offer.reference | string | "OFR-2026-118" |
| offer.date | date | "2026-09-17" |
| offer.respond_within_days | number | 8 |
| offer.currency | string | "GBP" |
| offer.locale | string | "en-GB" |
| role | object | |
| role.title | string | "Senior Product Designer" |
| role.department | string | "Product and Design" |
| role.reports_to | string | "Rafael Oduya, Head of Design" |
| role.start_date | date | "2026-10-19" |
| role.contract | string | "Permanent, full-time" |
| role.salary | number | 62500 |
| role.salary_period | string | "per year" |
| role.hours | string | "37.5 hours a week, Monday to Friday" |
| role.location | string | "Hybrid: London studio two days a week, remote …" |
| role.holiday | string | "27 days a year plus public holidays" |
| role.probation_months | number | 6 |
| role.notice | string | "One month during probation, three months after…" |
| sections | array<object> · 4 | |
| sections[].heading | string | "Your role" |
| sections[].body | string | "You will lead design for our scheduling produc…" |
| conditions | array<string> · 4 | "Two satisfactory references, one of them from …" |
| signatory | object | |
| signatory.name | string | "Hannah Okonjo-Reid" |
| signatory.title | string | "People Partner" |
Sample data
Fictional records used for previews. Replace them with your own data when you call the API.
{
"company": {
"name": "Lanternfish Software Ltd",
"trading_name": "Lanternfish",
"address": "3rd Floor, Brightwater House\n27 Leman Street\nLondon E1 8XY",
"phone": "+44 20 7946 0742",
"email": "people@lanternfish.example.com",
"website": "lanternfish.example.com",
"company_number": "12345678",
"registered_office": "3rd Floor, Brightwater House, 27 Leman Street, London E1 8XY"
},
"candidate": {
"title": "Ms",
"first_name": "Imogen",
"last_name": "Adeyemi",
"address": "Flat 3, 27 Wren Terrace\nLondon\nSE15 4XY"
},
"offer": {
"reference": "OFR-2026-118",
"date": "2026-09-17",
"respond_within_days": 8,
"currency": "GBP",
"locale": "en-GB"
},
"role": {
"title": "Senior Product Designer",
"department": "Product and Design",
"reports_to": "Rafael Oduya, Head of Design",
"start_date": "2026-10-19",
"contract": "Permanent, full-time",
"salary": 62500,
"salary_period": "per year",
"hours": "37.5 hours a week, Monday to Friday",
"location": "Hybrid: London studio two days a week, remote on other days",
"holiday": "27 days a year plus public holidays",
"probation_months": 6,
"notice": "One month during probation, three months afterwards"
},
"sections": [
{
"heading": "Your role",
"body": "You will lead design for our scheduling products, working with product managers and engineers from early research through to release. Your main responsibilities are described in the enclosed role profile, which we will review with you as the team grows."
},
{
"heading": "Pay and benefits",
"body": "- Your salary is paid monthly by bank transfer on the last working day of the month.\n- You can join the workplace pension scheme. We contribute **6%** of your salary when you contribute at least 4%.\n- Private medical cover starts after your first full month.\n- You have a learning budget of **£1,000 a year** for courses, books and conferences."
},
{
"heading": "Probation",
"body": "The first six months of your employment are a probationary period. You and your manager will meet monthly to review how things are going, and we will confirm the end of your probation in writing."
},
{
"heading": "Next steps",
"body": "Please sign and return the acceptance slip at the end of this letter. When we receive it, we will send your contract of employment, details of your first day and a short form for the checks listed below."
}
],
"conditions": [
"Two satisfactory references, one of them from your most recent employer",
"Proof of your right to work in the United Kingdom",
"Confirmation of the qualifications listed in your application",
"Your signature on our standard terms and conditions of employment"
],
"signatory": {
"name": "Hannah Okonjo-Reid",
"title": "People Partner"
}
}Source
The body markup with Jinja, the stylesheet placed in <head> and the header and footer settings.
{#- Offer of employment on letterhead. The role details table, the response deadline and the
acceptance slip all read from the same data, and the body sections are Markdown so each company
can supply its own wording. Sample wording only; it is not legal advice. -#}
{%- set locale = offer.locale | default("en-GB") -%}
{%- set respond_by = offer.date | date_add(days=offer.respond_within_days) -%}
{%- set full_name = candidate.first_name ~ " " ~ candidate.last_name -%}
{%- macro detail(label, value) -%}
<tr><th scope="row">{{ label }}</th><td>{{ value }}</td></tr>
{%- endmacro -%}
<header class="letterhead">
<div class="brand">
<svg class="logo" viewBox="0 0 48 32" aria-hidden="true">
<path d="M4 16c6-9 17-12 27-8l9-6v28l-9-6C21 28 10 25 4 16z" fill="#4b2a5e"/>
<circle cx="15" cy="14.5" r="2.2" fill="#ffffff"/>
<path d="M15 3.5c4 1.5 6 4.5 5.5 7.5" fill="none" stroke="#c9a7df" stroke-width="1.6" stroke-linecap="round"/>
<circle cx="21" cy="11.5" r="1.8" fill="#f2c14e"/>
</svg>
<span class="brand-name">{{ company.trading_name }}</span>
</div>
<div class="company-contact">
{{ company.address | nl2br }}<br>
{{ company.phone }} · {{ company.email }}
</div>
</header>
<section class="addressing">
<div class="recipient">
<p class="confidential">Private and confidential</p>
<p>{{ candidate.title }} {{ full_name }}</p>
<p>{{ candidate.address | nl2br }}</p>
</div>
<dl class="letter-meta">
<dt>Date</dt><dd>{{ offer.date | format_date("long", locale) }}</dd>
<dt>Our reference</dt><dd>{{ offer.reference }}</dd>
</dl>
</section>
<p class="salutation">Dear {{ candidate.first_name }},</p>
<h1 class="subject">Offer of employment: {{ role.title }}</h1>
<p class="intro">
Thank you for the time you spent with us during the interview process. I am delighted to offer you the role of
<strong>{{ role.title }}</strong> in our {{ role.department }} team at {{ company.name }}, starting on
{{ role.start_date | format_date("full", locale) }}. The main terms of the offer are set out below.
</p>
<table class="details">
<tbody>
{{ detail("Job title", role.title) }}
{{ detail("Department", role.department) }}
{{ detail("Reporting to", role.reports_to) }}
{{ detail("Start date", role.start_date | format_date("long", locale)) }}
{{ detail("Contract", role.contract) }}
{{ detail("Salary", (role.salary | format_currency(offer.currency, locale, 0)) ~ " " ~ role.salary_period ~ ", before tax") }}
{{ detail("Working hours", role.hours) }}
{{ detail("Location", role.location) }}
{{ detail("Holiday", role.holiday) }}
{{ detail("Probation", role.probation_months ~ " months") }}
{{ detail("Notice period", role.notice) }}
</tbody>
</table>
{% for section in sections %}
<section class="body-section">
<h2>{{ section.heading }}</h2>
{{ section.body | markdown }}
</section>
{% endfor %}
{% if conditions %}
<section class="body-section conditions">
<h2>Conditions of this offer</h2>
<p>This offer is conditional on the following:</p>
<ol>
{% for condition in conditions %}
<li><span class="cond-no">{{ loop.index }}</span>{{ condition }}</li>
{% endfor %}
</ol>
</section>
{% endif %}
<p class="respond">
Please let us know your decision by <strong>{{ respond_by | format_date("long", locale) }}</strong>. If you have any
questions in the meantime, contact me at {{ company.email }}. We are looking forward to working with you.
</p>
<section class="closing">
<p>Yours sincerely,</p>
<div class="signature-space"></div>
<p class="signatory-name">{{ signatory.name }}</p>
<p class="signatory-title">{{ signatory.title }}, {{ company.name }}</p>
</section>
<section class="tear-off">
<div class="cut-line">
<svg class="scissors" viewBox="0 0 24 24" aria-hidden="true">
<circle cx="6" cy="6" r="3" fill="none" stroke="#6b5a78" stroke-width="1.6"/>
<circle cx="6" cy="18" r="3" fill="none" stroke="#6b5a78" stroke-width="1.6"/>
<path d="M8.5 7.5L21 17M8.5 16.5L21 7" stroke="#6b5a78" stroke-width="1.6" stroke-linecap="round"/>
</svg>
<span>Detach and return</span>
</div>
<div class="slip">
<div class="slip-head">
<h2>Acceptance of offer</h2>
<span class="slip-ref">Ref. {{ offer.reference }}</span>
</div>
<p>
I, {{ full_name }}, accept the offer of employment as {{ role.title }} with {{ company.name }} on the terms set
out in the letter dated {{ offer.date | format_date("long", locale) }}, with a start date of
{{ role.start_date | format_date("long", locale) }}.
</p>
<div class="slip-fields">
<div class="field wide"><span>Signature</span></div>
<div class="field"><span>Date</span></div>
</div>
<p class="slip-note">Please return by {{ respond_by | format_date("long", locale) }} to {{ company.email }} or to the address at the top of this letter.</p>
</div>
</section><title>Offer of employment: {{ role.title }} – {{ candidate.first_name }} {{ candidate.last_name }}</title>
<style>
:root {
--ink: #221c27;
--muted: #6b6273;
--rule: #e3dce8;
--plum: #4b2a5e;
--plum-soft: #f3edf7;
--lilac: #c9a7df;
--gold: #f2c14e;
}
* { box-sizing: border-box; }
html { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
body {
margin: 0;
color: var(--ink);
font-family: "Source Sans 3", "Liberation Sans", Arial, sans-serif;
font-size: 10pt;
line-height: 1.5;
}
p { margin: 0; }
/* Letterhead */
.letterhead { display: flex; justify-content: space-between; align-items: flex-start; padding-bottom: 5mm; border-bottom: 0.8mm solid var(--plum); }
.brand { display: flex; align-items: center; gap: 3mm; }
.logo { width: 15mm; height: 10mm; }
.brand-name { font-family: "Merriweather", "Liberation Serif", Georgia, serif; font-size: 17pt; font-weight: 700; color: var(--plum); letter-spacing: -0.01em; }
.company-contact { text-align: right; font-size: 8pt; line-height: 1.45; color: var(--muted); }
/* Addressing */
.addressing { display: flex; justify-content: space-between; align-items: flex-start; margin: 10mm 0 9mm; }
.confidential {
display: inline-block; margin-bottom: 2.5mm; padding: 0.4mm 2mm; border: 0.3mm solid var(--plum); border-radius: 0.8mm;
font-size: 7pt; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--plum);
}
.letter-meta { display: grid; grid-template-columns: auto auto; gap: 1mm 4mm; margin: 0; font-size: 9pt; }
.letter-meta dt { color: var(--muted); text-align: right; }
.letter-meta dd { margin: 0; font-weight: 600; }
.salutation { margin-bottom: 3mm; }
.subject {
margin: 0 0 4mm; font-family: "Merriweather", "Liberation Serif", Georgia, serif; font-size: 14pt; font-weight: 700;
line-height: 1.3; color: var(--plum);
}
.intro { margin-bottom: 5mm; }
/* Role details */
.details { width: 100%; border-collapse: collapse; margin-bottom: 6mm; background: var(--plum-soft); border-radius: 1.5mm; }
.details tr { break-inside: avoid; page-break-inside: avoid; }
.details th, .details td { padding: 1.9mm 4mm; text-align: left; vertical-align: top; border-bottom: 0.25mm solid #e4d9ec; }
.details tr:last-child th, .details tr:last-child td { border-bottom: none; }
.details th { width: 38mm; font-weight: 600; color: var(--plum); }
/* Body */
.body-section { margin-bottom: 4.5mm; break-inside: avoid; page-break-inside: avoid; }
.body-section h2 {
margin: 0 0 1.2mm; font-size: 8.5pt; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--plum);
}
.body-section p { margin: 0 0 1.5mm; }
.body-section ul { margin: 0; padding-left: 5mm; }
.body-section li { margin-bottom: 0.8mm; }
.body-section li::marker { color: var(--plum); }
.conditions ol { margin: 1.5mm 0 0; padding: 0; list-style: none; }
.conditions li { display: flex; gap: 2.5mm; margin-bottom: 1.2mm; }
.cond-no {
flex: none; width: 5mm; height: 5mm; border-radius: 50%; background: var(--plum); color: #ffffff;
font-size: 7.5pt; font-weight: 700; line-height: 5mm; text-align: center;
}
.respond { margin: 5mm 0 6mm; }
.closing { break-inside: avoid; page-break-inside: avoid; }
.signature-space { height: 16mm; width: 60mm; border-bottom: 0.3mm solid var(--ink); }
.signatory-name { margin-top: 1.5mm; font-weight: 700; }
.signatory-title { color: var(--muted); }
/* Acceptance slip */
.tear-off { margin-top: 10mm; break-inside: avoid; page-break-inside: avoid; }
.cut-line { display: flex; align-items: center; gap: 2mm; border-top: 0.4mm dashed #9d8fa8; padding-top: 0; }
.scissors { width: 5mm; height: 5mm; margin-top: -2.6mm; background: #ffffff; }
.cut-line span { margin-top: -2.4mm; padding: 0 1.5mm; background: #ffffff; font-size: 7pt; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); }
.slip { margin-top: 4mm; padding: 5mm 6mm; border: 0.3mm solid var(--rule); border-radius: 2mm; }
.slip-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 2mm; }
.slip-head h2 { margin: 0; font-family: "Merriweather", "Liberation Serif", Georgia, serif; font-size: 12pt; color: var(--plum); }
.slip-ref { font-size: 8pt; color: var(--muted); }
.slip-fields { display: grid; grid-template-columns: 2fr 1fr; gap: 10mm; margin-top: 4mm; }
.field { position: relative; height: 15mm; border-bottom: 0.3mm solid var(--ink); }
.field span { position: absolute; left: 0; bottom: -4.5mm; font-size: 7pt; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
.slip-note { margin-top: 7mm; font-size: 8.5pt; color: var(--muted); }
</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": "Lanternfish Software Ltd",
"trading_name": "Lanternfish",
"address": "3rd Floor, Brightwater House\n27 Leman Street\nLondon E1 8XY",
"phone": "+44 20 7946 0742",
"email": "people@lanternfish.example.com",
"website": "lanternfish.example.com",
"company_number": "12345678",
"registered_office": "3rd Floor, Brightwater House, 27 Leman Street, London E1 8XY"
},
"candidate": {
"title": "Ms",
"first_name": "Imogen",
"last_name": "Adeyemi",
"address": "Flat 3, 27 Wren Terrace\nLondon\nSE15 4XY"
},
"offer": {
"reference": "OFR-2026-118",
"date": "2026-09-17",
"respond_within_days": 8,
"currency": "GBP",
"locale": "en-GB"
},
"role": {
"title": "Senior Product Designer",
"department": "Product and Design",
"reports_to": "Rafael Oduya, Head of Design",
"start_date": "2026-10-19",
"contract": "Permanent, full-time",
"salary": 62500,
"salary_period": "per year",
"hours": "37.5 hours a week, Monday to Friday",
"location": "Hybrid: London studio two days a week, remote on other days",
"holiday": "27 days a year plus public holidays",
"probation_months": 6,
"notice": "One month during probation, three months afterwards"
},
"sections": [
{
"heading": "Your role",
"body": "You will lead design for our scheduling products, working with product managers and engineers from early research through to release. Your main responsibilities are described in the enclosed role profile, which we will review with you as the team grows."
},
{
"heading": "Pay and benefits",
"body": "- Your salary is paid monthly by bank transfer on the last working day of the month.\n- You can join the workplace pension scheme. We contribute **6%** of your salary when you contribute at least 4%.\n- Private medical cover starts after your first full month.\n- You have a learning budget of **£1,000 a year** for courses, books and conferences."
}
],
"conditions": [
"Two satisfactory references, one of them from your most recent employer",
"Proof of your right to work in the United Kingdom"
],
"signatory": {
"name": "Hannah Okonjo-Reid",
"title": "People Partner"
}
},
"output": {
"format": "pdf",
"filename": "letter-offer-employment.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": "Lanternfish Software Ltd",
"trading_name": "Lanternfish",
"address": "3rd Floor, Brightwater House\n27 Leman Street\nLondon E1 8XY",
"phone": "+44 20 7946 0742",
"email": "people@lanternfish.example.com",
"website": "lanternfish.example.com",
"company_number": "12345678",
"registered_office": "3rd Floor, Brightwater House, 27 Leman Street, London E1 8XY"
},
"candidate": {
"title": "Ms",
"first_name": "Imogen",
"last_name": "Adeyemi",
"address": "Flat 3, 27 Wren Terrace\nLondon\nSE15 4XY"
},
"offer": {
"reference": "OFR-2026-118",
"date": "2026-09-17",
"respond_within_days": 8,
"currency": "GBP",
"locale": "en-GB"
},
"role": {
"title": "Senior Product Designer",
"department": "Product and Design",
"reports_to": "Rafael Oduya, Head of Design",
"start_date": "2026-10-19",
"contract": "Permanent, full-time",
"salary": 62500,
"salary_period": "per year",
"hours": "37.5 hours a week, Monday to Friday",
"location": "Hybrid: London studio two days a week, remote on other days",
"holiday": "27 days a year plus public holidays",
"probation_months": 6,
"notice": "One month during probation, three months afterwards"
},
"sections": [
{
"heading": "Your role",
"body": "You will lead design for our scheduling products, working with product managers and engineers from early research through to release. Your main responsibilities are described in the enclosed role profile, which we will review with you as the team grows."
},
{
"heading": "Pay and benefits",
"body": "- Your salary is paid monthly by bank transfer on the last working day of the month.\n- You can join the workplace pension scheme. We contribute **6%** of your salary when you contribute at least 4%.\n- Private medical cover starts after your first full month.\n- You have a learning budget of **£1,000 a year** for courses, books and conferences."
}
],
"conditions": [
"Two satisfactory references, one of them from your most recent employer",
"Proof of your right to work in the United Kingdom"
],
"signatory": {
"name": "Hannah Okonjo-Reid",
"title": "People Partner"
}
},
"output": {
"format": "pdf",
"filename": "letter-offer-employment.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": "Lanternfish Software Ltd",
"trading_name": "Lanternfish",
"address": "3rd Floor, Brightwater House\n27 Leman Street\nLondon E1 8XY",
"phone": "+44 20 7946 0742",
"email": "people@lanternfish.example.com",
"website": "lanternfish.example.com",
"company_number": "12345678",
"registered_office": "3rd Floor, Brightwater House, 27 Leman Street, London E1 8XY",
},
"candidate": {
"title": "Ms",
"first_name": "Imogen",
"last_name": "Adeyemi",
"address": "Flat 3, 27 Wren Terrace\nLondon\nSE15 4XY",
},
"offer": {
"reference": "OFR-2026-118",
"date": "2026-09-17",
"respond_within_days": 8,
"currency": "GBP",
"locale": "en-GB",
},
"role": {
"title": "Senior Product Designer",
"department": "Product and Design",
"reports_to": "Rafael Oduya, Head of Design",
"start_date": "2026-10-19",
"contract": "Permanent, full-time",
"salary": 62500,
"salary_period": "per year",
"hours": "37.5 hours a week, Monday to Friday",
"location": "Hybrid: London studio two days a week, remote on other days",
"holiday": "27 days a year plus public holidays",
"probation_months": 6,
"notice": "One month during probation, three months afterwards",
},
"sections": [
{
"heading": "Your role",
"body": "You will lead design for our scheduling products, working with product managers and engineers from early research through to release. Your main responsibilities are described in the enclosed role profile, which we will review with you as the team grows.",
},
{
"heading": "Pay and benefits",
"body": "- Your salary is paid monthly by bank transfer on the last working day of the month.\n- You can join the workplace pension scheme. We contribute **6%** of your salary when you contribute at least 4%.\n- Private medical cover starts after your first full month.\n- You have a learning budget of **£1,000 a year** for courses, books and conferences.",
},
],
"conditions": ["Two satisfactory references, one of them from your most recent employer", "Proof of your right to work in the United Kingdom"],
"signatory": {
"name": "Hannah Okonjo-Reid",
"title": "People Partner",
},
},
"output": {
"format": "pdf",
"filename": "letter-offer-employment.pdf",
},
},
timeout=90,
)
response.raise_for_status()
print(response.json())