Design

Markdown templates

Write a template in Markdown, add Carbone tags, and generate it from JSON data as a PDF, a Word file or an image.
COMMUNITY FEATURE Available for:
Carbone Cloud
Carbone On-premise
Embedded Carbone JS
  v5.0+ 

Any Markdown file can be a template. Write Carbone tags in its text and tables, send your JSON data to the API, and convert the Markdown to PDF, DOCX, ODT, PNG or JPG. AI models write Markdown natively, so Carbone turns their output into finished documents.

Basic

Carbone needs two elements to generate a document:

In {d.user.firstName}, d is the root of the JSON data and user.firstName is the path to the value. This path must match the structure of your JSON. Use any valid Markdown structure: Carbone replaces only the tags and keeps the rest of the text.

data
{
  "user": {
    "firstName": "John",
    "lastName": "Doe",
    "email": "john.doe@carbone.io"
  },
  "membershipLevel": "Premium"
}
template
# Welcome, {d.user.firstName} {d.user.lastName}!Thank you for registering with us.Your email address is: {d.user.email}Your membership level: {d.membershipLevel}
Carbone Merge Icon
result
# Welcome, John Doe!Thank you for registering with us.Your email address is: john.doe@carbone.ioYour membership level: Premium

Style

A Markdown template can take the look of a DOCX or ODT template: text styles, page, header and footer. Design your company layout once in Word or LibreOffice, and every Markdown document uses it, including reports drafted by an AI model.

  1. Create the style template in Word or LibreOffice. Style the headings, the body text and the table text, then set up the page, header and footer.
  2. Upload it in Carbone Studio or with the upload API. Copy its template ID to use the latest deployed version, or its version ID to use one exact version.
  3. Add the tag {o.styleSource=ID} anywhere in the Markdown template, for example on the first line.
data.jsonDownload
{
  "quarter": "Q3 2026",
  "publishedAt": "2026-10-12",
  "revenue": 4820000,
  "growth": 18.4,
  "newCustomers": 126,
  "customers": 2140,
  "retention": 112,
  "regions": {
    "northAmerica": 2310000,
    "europe": 1480000,
    "asiaPacific": 720000,
    "latinAmerica": 310000
  }
}
template.mdDownload
Markdown template of a quarterly report with Carbone tags and the styleSource option, and its Word style template with a logo header and page numbers
style.docxDownload
Markdown template of a quarterly report with Carbone tags and the styleSource option, and its Word style template with a logo header and page numbers, style.docx
report.pdfDownload
PDF quarterly report generated by Carbone from Markdown, with the logo header, the footer and the heading styles of the Word style template

The style template sets:

The style template is also a Carbone template. Its tags receive the data of the report, like {d.quarter} in the header. Its body is not copied into the report.

The style is applied when the Markdown template is converted to PDF. Table borders are not part of the style: tables keep thin black borders.

Loops

A loop repeats a part of the template for each item of an array in your JSON data. In Markdown templates, loops are supported inside tables. Insert the loop tags inside table cells, start the repeated row with its first tag (no leading |), and place the end iterator tag alone on the next line.

A loop needs two tags:

All the content between the start and end tags is repeated: text, Carbone tags and styles.

data
{
  "users": [
    { "id": 1, "name": "Alice", "role": "Admin" },
    { "id": 2, "name": "Bob", "role": "Editor" },
    { "id": 3, "name": "Charlie", "role": "Viewer" }
  ]
}
template
# User ListID | Name | Role
-- | ---- | ----
{d.users[i].id} | {d.users[i].name} | {d.users[i].role}
{d.users[i+1]}
Carbone Merge Icon
result
# User ListID | Name | Role
-- | ---- | ----
1 | Alice | Admin
2 | Bob | Editor
3 | Charlie | Viewer

Carbone loops need no keyword like "for" or "foreach". The tags define the loop.

Conditions

Conditions show or hide content based on the data:

Inline conditions

Use :ifEQ, :show and :elseShow to print one value or another, depending on the data.

data
{
  "status": "active",
  "score": 95,
  "isUrgent": true
}
template
Status: {d.status:ifEQ('active'):show('✅ Active'):elseShow('❌ Inactive')}Score: {d.score:ifGT(90):show('Excellent'):elseShow('Good')}Priority: {d.isUrgent:ifEQ(true):show('**HIGH**'):elseShow('Normal')}
Carbone Merge Icon
result
Status: ✅ ActiveScore: ExcellentPriority: **HIGH**

Conditional sections

Use :showBegin/:showEnd or :hideBegin/:hideEnd to show or hide a whole section, with its paragraphs, tables or headings, depending on the data:

showBegin / showEnd Example

The showBegin/showEnd formatters show the details section, with its list, only if the showDetails field is true: here it is false, so the section is removed.

data
{
  "showDetails": false,
  "product": "Carbone Pro"
}
template
Product: {d.product}{d.showDetails:ifEQ(true):showBegin}
## Details- Feature 1
- Feature 2{d.showDetails:ifEQ(true):showEnd}Thank you for using Carbone!
Carbone Merge Icon
result
Product: Carbone ProThank you for using Carbone!

hideBegin / hideEnd Example

The greetings section is deleted when the field name is undefined/null/empty.

data
{
  "name": null
}
template
## Dashboard{d.name:ifEM:hideBegin}
Welcome, **{d.name}**!
{d.name:ifEM:hideEnd}
Carbone Merge Icon
result
## Dashboard

Drop / keep via HTML blocks

⚠️ Coming soon: Drop / Keep via HTML blocks is not yet supported in Markdown templates.

Pictures

Write a tag as the source of a Markdown image to insert a picture from the data:

![Alt text]({d.imageUrl})

The image source can be:

data
{
  "userProfile": {
    "name": "John Doe",
    "profilePictureUrl": "https://carbone.io/images/john-doe.jpg",
    "profilePictureBase64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD..."
  }
}
template
# Welcome, {d.userProfile.name}!![Profile Picture]({d.userProfile.profilePictureUrl})
Carbone Merge Icon
result
# Welcome, John Doe!![Profile Picture](https://carbone.io/images/john-doe.jpg)

To set the size of the image, write an HTML <img> tag in the template:

<img src="{d.userProfile.profilePictureUrl}" width="200" height="auto" alt="Profile Picture">

Charts

The :chart formatter draws a chart from an ECharts configuration object in the data. Chain it to the tag of this object, and use the tag as the image source: Carbone replaces it with an SVG image of the chart.

Use an HTML <img> tag to set the width and height of the chart in the converted PDF, DOCX or ODT:

<img src="{d.chartOptions:chart}" width="600" height="400" alt="Sales Overview"/>

The Markdown image syntax ![Sales Overview]({d.chartOptions:chart}) also works, but it cannot set the size, and the chart appears as a very small image.

The top-level width and height properties of the ECharts configuration set the size of the generated image, not its size in the document. Set the size in the document with the width and height attributes of the <img> tag.

Learn more about charts.

Colors

Markdown has no syntax for colors. Write tags in the style attribute of an HTML element that starts the line, like <p> or <div>, to set the colors of the text from the data.

data
{
  "theme": "dark",
  "textColor": "#ffffff",
  "bgColor": "#1a1a1a",
  "alertColor": "#ff4444",
  "isUrgent": true
}
template
## Status Report<p style="color: {d.alertColor};">{d.isUrgent:ifEQ(true):show('⚠️ Urgent Alert'):elseShow('All Clear')}</p><div style="color: {d.textColor}; background-color: {d.bgColor}; padding: 10px;">
  This section uses colors from the dataset.
</div>Theme: {d.theme:ifEQ('dark'):show('🌙 Dark Mode'):elseShow('☀️ Light Mode')}
Carbone Merge Icon
result
## Status Report<p style="color: #ff4444;">⚠️ Urgent Alert</p><div style="color: #ffffff; background-color: #1a1a1a; padding: 10px;">
  This section uses colors from the dataset.
</div>Theme: 🌙 Dark Mode

To color a word inside a sentence, put the <span> in a <p> element: <p>Status: <span style="color: {d.alertColor};">{d.status}</span></p>.

Formatters

Formatters transform a value before Carbone prints it. Chain them to a tag with :, like {d.value:formatterName(args)}. The sections below cover the most common formatters, for dates, numbers and currencies. For the full list, see the Formatters documentation.

Date formatting

:formatD() prints a date in the format of your choice. The pattern uses Day.js tokens, like YYYY, MM, DD or dddd.

data
{
  "orderDate": "2025-03-15T14:30:00Z",
  "deliveryDate": "2025-04-01T00:00:00Z"
}
template
## Order SummaryOrder date: {d.orderDate:formatD('DD/MM/YYYY')}
Delivery date: {d.deliveryDate:formatD('dddd DD MMMM YYYY')}
Carbone Merge Icon
result
## Order SummaryOrder date: 15/03/2025
Delivery date: Tuesday 01 April 2025

For all the date patterns and options, see the Date formatter documentation.

Number formatting

:formatN() sets the number of decimals and adds the thousands separators. The output follows the lang option passed at render time.

data
{
  "quantity": 1234567.891,
  "rating": 4.5
}
template
Quantity: {d.quantity:formatN(2)}
Rating: {d.rating:formatN(1)}
Carbone Merge Icon
result
Quantity: 1,234,567.89
Rating: 4.5

For all the options, see the Number formatter documentation.

Currency formatting

:formatC() prints an amount with its currency symbol and decimals, formatted for the language of the report.

data
{
  "price": 4999.99,
  "discount": 250
}
template
Price: {d.price:formatC(2, 'EUR')}
Discount: {d.discount:formatC(2, 'EUR')}
Carbone Merge Icon
result
Price: 4,999.99 €
Discount: 250.00 €

For all the currencies and options, see the Currency formatter documentation.

Barcodes

The :barcode(type) formatter draws a barcode from a value. type is the barcode symbology, like qrcode, ean13 or code128.

Use an HTML <img> tag to set the width and height of the barcode in the converted PDF, DOCX or ODT:

<img src="{d.productCode:barcode(qrcode)}" width="150" height="150" alt="Product QR code"/>

The Markdown image syntax ![Product QR code]({d.productCode:barcode(qrcode)}) also works, but it cannot set the size, and the barcode appears as a very small image.

Pass the barcode options as extra arguments, like {d.productCode:barcode(qrcode, includetext:false)}. Their width and height options set the size of the generated barcode, not its size in the document. Set the size in the document with the width and height attributes of the <img> tag.

Learn more about barcodes.

Write tags in the address or the text of a Markdown link to build personalized links from the data.

data
{
    "documentationUrl": "https://carbone.io/documentation/",
    "isLoggedIn": true,
    "profileUrl": "https://account.carbone.io/",
    "defaultUrl": "https://account.carbone.io/login",
    "product": {
        "name": "Carbone Cloud",
        "url": "https://carbone.io/pricing.html"
    }
}
template
<!-- Directly inject a URL -->
[Read the Docs]({d.documentationUrl})<!-- Conditionally set a URL -->
[{d.isLoggedIn:ifEQ(true):show('Your Profile'):elseShow('Login')}]({d.isLoggedIn:ifEQ(true):show(d.profileUrl):elseShow(d.defaultUrl)})<!-- Inject a URL from a nested object -->
[Learn about {d.product.name}]({d.product.url})<!-- Combine static and dynamic paths -->
[Documentation](/docs/{d.product.name:lowerCase():replace(' ', '-')})
Carbone Merge Icon
result
[Read the Docs](https://carbone.io/documentation/)[Your Profile](https://account.carbone.io/)[Learn about Carbone Cloud](https://carbone.io/pricing.html)[Documentation](/docs/carbone-cloud)

Inject HTML in Markdown

By default, Carbone encodes the < and > characters of a value, so HTML from the data, like {d.richText}, is printed as literal text. Add the :html formatter to render it as formatted content in the converted document.

data
{
  "richText": "<p>The engine is <u>powered</u> by <i>cryogenic liquid methane</i> and <b>liquid oxygen</b> (LOX).</p>"
}
template
### Engine{d.richText:html}
Carbone Merge Icon
result
### Engine<p>The engine is <u>powered</u> by <i>cryogenic liquid methane</i> and <b>liquid oxygen</b> (LOX).</p>

Learn more about the :html formatter.

Translations and i18n

One Markdown template can generate documents in any language. Define a localization dictionary and pass the lang option at render time. Carbone replaces the text with its translation:

:formatD(), :formatN() and :formatC() follow the language too.

data
{
  "data"         : { "date": "2025-03-15", "tool": "key1", "protection": "key2" },
  "convertTo"    : "md",
  "lang"         : "en-us",
  "translations" : {
    "fr-fr" : {
      "Invoice"    : "Facture",
      "Date"       : "Date",
      "key1"       : "Tournevis",
      "key2"       : "Gants"
    },
    "en-us" : {
      "Invoice"    : "Invoice",
      "Date"       : "Date",
      "key1"       : "Screwdrivers",
      "key2"       : "Gloves"
    }
  }
}
template
# {t(Invoice)}{t(Date)}: {d.date:formatD('DD/MM/YYYY')}| Item | Quantity |
| ---- | -------- |
| {d.tool:t} | 1 |
| {d.protection:t} | 2 |
Carbone Merge Icon
result
# InvoiceDate: 15/03/2025| Item | Quantity |
| ---- | -------- |
| Screwdrivers | 1 |
| Gloves | 2 |

Pass the localization dictionary in the translations field of the API request body. For the complete reference, including the CLI tools and the dictionary generation, see the Translations i18n documentation.

Comments

Carbone reads all the text of the template, including HTML comments (<!-- -->). A tag inside a comment, like {d.user.name}, is replaced with data before the Markdown is rendered.

To mention a tag in a note, break its syntax so Carbone ignores it: <!-- Note: Use d.user.name (escaped) for the username -->.

data
{
  "user": {
    "name": "John"
  }
}
template
# User Profile<!-- Printing Carbone Tags within Comments: -->
<!-- {d.user.name} -->
Carbone Merge Icon
result
# User Profile<!-- Printing Carbone Tags within Comments: -->
<!-- John -->

Automate a Markdown file

Store the template once, then generate a Markdown file for each set of data. These commands use the files of the Style example.

1. Upload the template. Carbone returns its template ID in data.id:

curl --request POST 'https://api.carbone.io/template' \
  --header 'carbone-version: 5' \
  --header 'Authorization: Bearer API_TOKEN' \
  --form 'versioning=true' \
  --form 'template=@quarterly-report-template.md'

2. Generate the document with the template ID and the JSON data:

curl --request POST 'https://api.carbone.io/render/TEMPLATE_ID?download=true' \
  --header 'carbone-version: 5' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer API_TOKEN' \
  --data-raw "{ \"data\": $(cat quarterly-report-data.json) }" \
  --output report.md

Without convertTo, the report has the format of the template. To get a PDF or a DOCX, see Convert Markdown to PDF. Learn more about template storage.

Convert Markdown to PDF

Using Carbone with AI-generated content: AI assistants like ChatGPT, Gemini and Mistral Vibe write Markdown natively. Ask the assistant for a structured document, add Carbone tags for the dynamic fields, like {d.clientName} or {d.date}, then call the Carbone API with your JSON data to render the PDF or DOCX. It fits automated reports, personalized proposals, AI-drafted contracts and weekly summaries.

To generate a PDF from a Markdown template with the Carbone API:

  1. Prepare two files: a Markdown template with Carbone tags, like {d.name}, and the JSON data to populate it.
  2. Generate the PDF in one of three ways:
    • Use an official Carbone integration, like N8N, Make, Zapier, Airtable or Bubble.
    • Use a Carbone SDK: Node.js, Go, Python, Java, JavaScript or PHP.
    • Send a POST /render/template HTTP request to the Carbone API, like this curl command:
curl --location --request POST 'https://api.carbone.io/render/template?download=true' \
  --header 'carbone-version: 5' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer API_TOKEN' \
  --data-raw "{
    \"data\": {},
    \"template\": \"$(base64 -i document.md)\",
    \"convertTo\": \"pdf\",
    \"converter\": \"L\"
  }" \
  --output result.pdf
💡 Linux users: replace base64 -i document.md with base64 -w 0 document.md

Key parameters

Parameter Description
template Your Markdown template, encoded as a Base64 string.
data JSON data used to populate the template.
convertTo Output format: pdf, docx, odt, jpg, png or md.
?download=true Query parameter to download the document as a stream.
Authorization Header required for the Carbone Cloud API. Get your API key here.

For more details, refer to the Carbone API Documentation.

Markdown templates do not support the PDF conversion options of HTML templates, like the paper size, margins, headers and footers. Set them with a style template instead, or use an HTML template for advanced control of the PDF layout.