Guide
Business Documents in Markdown: Invoices, Proposals, Contracts and Case Studies
Published July 30, 2026
Client-facing documents have a property internal documents do not: someone outside your organisation judges your competence partly by how they look. They also tend to be highly repetitive — an invoice differs from last month’s by a few numbers — which makes them ideal candidates for a text-based, templated workflow.
This guide covers the four commonest client documents, with working templates. It also covers, honestly, the cases where you should reach for accounting software or a contract platform instead.
Invoices
An invoice is a structured legal document, not a letter. Getting the structure right matters more than the styling.
What an invoice needs
Requirements vary by jurisdiction, and you should check yours, but the near-universal core is:
- The word “Invoice” — genuinely required in several jurisdictions to distinguish it from a quote or a statement
- A unique, sequential invoice number
- Issue date, and payment due date or terms
- Your legal business name, address, and tax registration number where applicable
- The client’s legal name and address
- A line-item breakdown: description, quantity, unit price, line total
- Subtotal, tax (itemised by rate), and grand total
- Payment instructions — bank details, accepted methods, reference to quote
Two notes that catch people out. Sequential numbering is a legal requirement in much of the EU and elsewhere — gaps in the sequence are an audit flag, so never delete an invoice; issue a credit note. And if you are not VAT/GST registered, do not put a tax line on the invoice at all; charging tax you are not registered to collect is a real problem.
The template
<div class="invoice">
<div class="invoice-head">
# Invoice
**Invoice number:** 2026-0148
**Issue date:** 7 May 2026
**Due date:** 6 June 2026
**Terms:** Net 30
</div>
<div class="parties">
<div class="from">
**From**
Rivera Consulting Ltd
Unit 4, 18 Fenchurch Street
London EC3M 3BY
United Kingdom
VAT: GB123456789
</div>
<div class="to">
**Bill to**
Northwind Trading GmbH
Rosenthaler Straße 40
10178 Berlin
Germany
VAT: DE987654321
</div>
</div>
## Services
| Description | Qty | Unit | Amount |
| --- | ---: | ---: | ---: |
| Platform architecture review | 1 | £4,800.00 | £4,800.00 |
| Migration planning workshop (2 days) | 2 | £1,600.00 | £3,200.00 |
| Follow-up advisory, April | 12 | £180.00 | £2,160.00 |
<div class="totals">
| | |
| --- | ---: |
| Subtotal | £10,160.00 |
| VAT (reverse charge — see note) | £0.00 |
| **Total due** | **£10,160.00** |
</div>
<div class="note">
VAT reverse charge applies: customer to account for VAT under Article 196 of
Council Directive 2006/112/EC.
</div>
## Payment
**Bank:** Example Bank plc
**Account name:** Rivera Consulting Ltd
**IBAN:** GB29 EXMP 6016 1331 9268 19
**BIC:** EXMPGB2L
**Reference:** 2026-0148
Payment due within 30 days. Late payments accrue interest at 8% above the
Bank of England base rate under the Late Payment of Commercial Debts
(Interest) Act 1998.
</div>
Remember blank lines inside every div or the Markdown tables inside them will not parse.
Invoice CSS
@page {
size: A4;
margin: 18mm 18mm 22mm;
@bottom-center {
content: "Invoice 2026-0148 · Page " counter(page) " of " counter(pages);
font-size: 8pt;
color: #888;
}
}
@media print {
body {
font-family: "Inter", Helvetica, Arial, sans-serif;
font-size: 10pt;
line-height: 1.5;
color: #1a1a1a;
}
.invoice-head h1 {
font-size: 26pt;
letter-spacing: -0.02em;
margin: 0 0 8pt;
color: #111;
}
.invoice-head p {
font-size: 9.5pt;
line-height: 1.7;
}
/* Sender and recipient side by side. */
.parties {
display: flex;
gap: 20mm;
margin: 14pt 0 18pt;
padding: 10pt 0;
border-top: 0.75pt solid #e4e4e7;
border-bottom: 0.75pt solid #e4e4e7;
break-inside: avoid;
}
.parties > div { flex: 1; }
.parties p {
font-size: 9pt;
line-height: 1.55;
margin: 0;
}
h2 {
font-size: 9.5pt;
text-transform: uppercase;
letter-spacing: 0.08em;
color: #666;
margin: 16pt 0 5pt;
break-after: avoid;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 9.5pt;
font-variant-numeric: tabular-nums;
}
thead { display: table-header-group; }
th {
text-align: left;
font-size: 8.5pt;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #666;
border-bottom: 1pt solid #333;
padding: 4pt 6pt;
}
th:not(:first-child) { text-align: right; }
td {
padding: 5pt 6pt;
border-bottom: 0.5pt solid #ebebed;
}
/* Totals block, right-aligned and narrow. */
.totals {
margin-top: 12pt;
display: flex;
justify-content: flex-end;
break-inside: avoid;
}
.totals table {
width: 62mm;
}
.totals thead { display: none; }
.totals td {
border: none;
padding: 3pt 6pt;
}
.totals tr:last-child td {
border-top: 1pt solid #333;
font-size: 11pt;
padding-top: 6pt;
}
.note {
margin-top: 14pt;
padding: 8pt 10pt;
background: #f7f7f9;
border-left: 2.5pt solid #d4d4d8;
font-size: 8.5pt;
color: #444;
print-color-adjust: exact;
-webkit-print-color-adjust: exact;
}
}
.totals thead { display: none } is a workaround worth explaining: a Markdown table always emits a header row, and the totals block does not want one. Hiding it is simpler than dropping to raw HTML for that table.
Reusable templates
The value of a text invoice is that next month’s differs by a few lines. Keep a template with obvious placeholders:
**Invoice number:** {{INVOICE_NO}}
**Issue date:** {{ISSUE_DATE}}
**Due date:** {{DUE_DATE}}
Then a trivial script fills them:
#!/usr/bin/env bash
# new-invoice.sh 2026-0149 "Northwind Trading GmbH"
set -euo pipefail
NUMBER="$1"
ISSUE=$(date +"%-d %B %Y")
DUE=$(date -d "+30 days" +"%-d %B %Y")
sed -e "s/{{INVOICE_NO}}/$NUMBER/g" \
-e "s/{{ISSUE_DATE}}/$ISSUE/g" \
-e "s/{{DUE_DATE}}/$DUE/g" \
templates/invoice.md > "invoices/$NUMBER.md"
echo "Created invoices/$NUMBER.md"
On macOS use date -v+30d rather than date -d. For anything more elaborate than substitution, a small Python or Node script with a real template engine is better — see the automation guide.
Keeping invoices in Git gives you a complete, timestamped, immutable-by-convention record, which is genuinely useful at audit time.
When to use accounting software instead
Be honest with yourself about the threshold. Markdown invoices work well for a freelancer or small consultancy issuing a handful per month to a stable client list. They stop being appropriate when you need:
- Payment tracking. Knowing what is outstanding, what is overdue, and chasing it. A folder of PDFs cannot tell you this.
- Recurring billing or usage-based amounts.
- Multi-currency with exchange-rate handling at the transaction date.
- Tax filing integration. Most jurisdictions now expect digital submission that pulls from your accounting records.
- Sequential numbering guarantees across multiple people issuing invoices.
At that point use accounting software. Fighting a text pipeline into becoming a ledger is a poor use of your time, and getting tax filing wrong is expensive. Invoicing from Markdown is a legitimate choice at small scale, not a scalable accounting strategy.
Client proposals
A proposal is a persuasive document with a conventional shape. The structure below reflects what actually gets read: decision-makers read the summary and the price, then skim for risk.
<div class="cover">
# Platform Modernisation Proposal
## Prepared for Northwind Trading GmbH
**Prepared by:** Rivera Consulting Ltd
**Date:** 7 May 2026
**Valid until:** 7 June 2026
**Reference:** PROP-2026-031
</div>
## Executive summary
Northwind's order management system processes 40,000 orders daily on
infrastructure designed for 4,000. Peak-season failures in November 2025 cost
an estimated €280,000 in abandoned carts. We propose a three-phase
modernisation over 14 weeks, delivered without a service interruption, at a
fixed fee of €96,000.
## Your situation
Describe the problem in the client's own words, using specifics from your
discovery conversations. This section demonstrates you listened. It should
contain no solutions.
## Proposed approach
### Phase 1 — Assessment and instrumentation (weeks 1–3)
Establish baseline performance data and identify the specific bottlenecks.
Deliverable: a written assessment with prioritised findings.
### Phase 2 — Data layer consolidation (weeks 4–10)
Migrate order storage from the current schema to a partitioned model.
Deliverable: migrated production system, rollback plan, runbook.
### Phase 3 — Load validation (weeks 11–14)
Synthetic load testing at 3× current peak. Deliverable: validated capacity
report and monitoring dashboards.
## Timeline
| Phase | Weeks | Key deliverable |
| --- | --- | --- |
| Assessment | 1–3 | Written assessment and prioritised findings |
| Consolidation | 4–10 | Migrated production system |
| Validation | 11–14 | Capacity report, dashboards |
## Investment
| Item | Amount |
| --- | ---: |
| Phase 1 — Assessment | €18,000 |
| Phase 2 — Consolidation | €58,000 |
| Phase 3 — Validation | €20,000 |
| **Total** | **€96,000** |
Fixed fee. Invoiced on phase completion, net 30. Excludes third-party
infrastructure costs, estimated at €1,200/month during phases 2–3.
## What we need from you
- A named technical contact available for two hours weekly
- Read access to production monitoring by week 1
- A staging environment mirroring production schema by week 4
## Assumptions and exclusions
- Current schema documentation is accurate as supplied on 22 April 2026
- No changes to the public API contract are required
- Frontend work is out of scope
## About us
Two paragraphs of relevant credibility. Specific, comparable engagements —
not a company history.
## Next steps
Sign below, or reply to confirm and we will issue a contract. This proposal
is valid until 7 June 2026.
The two sections people omit and should not: “What we need from you” converts a proposal from a promise into a mutual commitment and pre-empts the commonest cause of overrun. “Assumptions and exclusions” is where you protect yourself; every assumption listed is a scope argument you do not have later.
Contracts and signature blocks
Markdown handles contract text well — numbered clauses, definitions, nested subclauses are all just lists and headings. Automatic clause numbering via CSS counters keeps references stable when you insert a clause:
.contract { counter-reset: clause; }
.contract h2 {
counter-increment: clause;
counter-reset: subclause;
}
.contract h2::before {
content: counter(clause) ". ";
}
.contract h3 {
counter-increment: subclause;
}
.contract h3::before {
content: counter(clause) "." counter(subclause) " ";
}
Write plain headings and get 1., 1.1, 1.2, 2. automatically. Insert a clause and everything renumbers.
A signature block:
<div class="signatures">
<div class="sig">
**For Rivera Consulting Ltd**
<div class="sig-line"></div>
Name:
Title:
Date:
</div>
<div class="sig">
**For Northwind Trading GmbH**
<div class="sig-line"></div>
Name:
Title:
Date:
</div>
</div>
.signatures {
display: flex;
gap: 18mm;
margin-top: 26pt;
break-inside: avoid;
page-break-inside: avoid;
}
.signatures .sig { flex: 1; }
.sig-line {
border-bottom: 0.75pt solid #333;
height: 34pt;
margin-bottom: 5pt;
}
.sig p {
font-size: 9pt;
line-height: 2.1;
color: #444;
}
break-inside: avoid is essential here. A signature block split across pages, with the line on one page and “Name:” on the next, looks careless on a document someone is about to sign.
The honest caveat about contracts
Producing a contract PDF from Markdown is fine. Everything after that is not a document-formatting problem:
- Legal review. A template you found online is not a contract reviewed for your jurisdiction and your risk. This guide is about formatting, not law.
- Signature validity. A printed signature line requires wet signing, scanning and returning — slow, and the resulting artefact is a scan of a document rather than a verifiable record. E-signature platforms (DocuSign, Dropbox Sign, and others) provide audit trails, identity verification, and tamper-evidence that a PDF does not.
- Versioning during negotiation. Git handles this beautifully for you and not at all for the counterparty, who will send back a Word document with tracked changes.
A realistic hybrid: draft and version in Markdown, export to PDF or DOCX, and route the final through an e-signature platform. That is a good workflow. Emailing a Markdown-generated PDF and asking for a scan back is worse than the alternatives.
Case studies
A case study is a marketing document with a fixed narrative arc, and the arc is the whole value: problem → what you did → measurable result.
<div class="case-study">
<div class="hero">
# 3× order throughput without downtime
**Northwind Trading** · E-commerce · 240 employees
</div>
<div class="metrics">
| 40,000 → 120,000 | 18h → 4min | €0 |
| --- | --- | --- |
| Daily orders handled | Reconciliation lag | Revenue lost to downtime |
</div>
## The challenge
Two paragraphs, concrete and specific. Name the business consequence, not
just the technical one: "November peak failures cost an estimated €280,000
in abandoned carts."
## What we did
Three or four paragraphs. Enough technical detail to be credible to a
technical reader, framed in terms of the outcome for a non-technical one.
> The migration ran over a weekend and nobody noticed — which is exactly
> what we wanted. Monday's peak was the first time the system had headroom
> in two years.
>
> — Katrin Mueller, CTO, Northwind Trading
## The results
- Sustained throughput increased from 40,000 to 120,000 daily orders
- Reconciliation lag reduced from 18 hours to under 4 minutes
- Zero downtime during migration
- Infrastructure cost per order down 31%
## Technical detail
An optional deeper section for readers who want it. Keeps the main narrative
readable while giving technical evaluators something substantial.
</div>
.case-study .hero {
padding-bottom: 12pt;
border-bottom: 1.5pt solid #111;
margin-bottom: 14pt;
}
.case-study .hero h1 {
font-size: 27pt;
line-height: 1.15;
letter-spacing: -0.025em;
margin: 0 0 6pt;
}
/* Headline metrics as a three-up row. */
.metrics table {
width: 100%;
border-collapse: collapse;
break-inside: avoid;
margin: 0 0 18pt;
}
.metrics th {
font-size: 19pt;
font-weight: 700;
letter-spacing: -0.02em;
color: #111;
text-align: center;
border: none;
border-top: 0.75pt solid #e4e4e7;
border-bottom: 0.75pt solid #e4e4e7;
padding: 12pt 6pt 3pt;
text-transform: none;
}
.metrics td {
font-size: 8.5pt;
color: #666;
text-align: center;
border: none;
border-bottom: 0.75pt solid #e4e4e7;
padding: 0 6pt 12pt;
}
.case-study blockquote {
border-left: 2.5pt solid #1d4ed8;
padding-left: 12pt;
margin: 16pt 0;
font-size: 11pt;
line-height: 1.5;
color: #333;
font-style: normal;
break-inside: avoid;
}
The metrics row inverts a Markdown table’s semantics — the header row holds the big numbers and the body row holds the labels — because a Markdown table always emits thead first and you want the numbers on top. It is a small hack that avoids raw HTML.
One content note: get written approval before publishing a client’s name and numbers. Approval for a case study is not implied by having done the work.
Try it yourself
The editor here handles all four of these — the CSS above can be pasted in directly, and the paginated preview shows exactly where signature blocks and totals land relative to page boundaries, which is what you actually need to check on client documents.
Because conversion runs entirely in your browser with nothing uploaded, client rates, bank details and contract terms never leave your machine — worth noting for exactly this category of document.
Related: team documentation for internal documents, and automation for generating invoices on a schedule.
About this guide
Written and maintained by the developer of MarkdownToFile — the browser-based converter this site runs on. Techniques described here are tested against the engines named in the text (Chromium's print pipeline, Paged.js, WeasyPrint), and engine limitations are stated rather than glossed over. Corrections are welcome via the contact page; more about the project on the about page.
Try it yourself — free, no signup
Convert your Markdown to a polished PDF right in your browser.
Open the editor