---
source: https://carbone.io/documentation/design/template-formats/idml.html
title: "IDML Templates | InDesign Data Merge from JSON | Carbone"
description: "Generate InDesign documents from IDML templates and JSON with the Carbone API: catalogs, price lists, badges and annual reports, with loops and conditions."
generated_at: "2026-11-11"
---

# IDML templates

Design a document in Adobe InDesign, add Carbone tags, and generate it from JSON data as an IDML file.  
COMMUNITY FEATURE Available for:  
✓ Carbone Cloud  
✓ Carbone On-premise  
✓ Embedded Carbone JS   v3.0+ 

Any IDML file can be a template. Design the document in Adobe InDesign, write Carbone tags in its text frames, tables and parent pages, and export it as InDesign Markup (File > Export > InDesign Markup (IDML)). Send your JSON data to the API: you get back an IDML file with the layout, styles and colors of the template, ready to open in InDesign and export to PDF.

## Basic

Write a tag in a text frame to insert a value from the JSON data. In `{d.name}`, `d` is the root of the data and `name` is the path to the value. The value takes the formatting of its tag: paragraph style, character style, size and color, like the product name in 50 pt.

**Data** ([basic-data.json](/file/idml/basic-data.json)):

```json
{
  "category": "Wireless speaker",
  "name": "Tessera One",
  "tagline": "Room-filling sound from a speaker the size of a book.",
  "price": 349,
  "monthly": 29.08,
  "release": "2026-10-15",
  "battery": 24,
  "weight": 1.8,
  "range": 30,
  "dealer": {
    "name": "Northwind Audio",
    "city": "Seattle",
    "website": "northwind.example",
    "url": "https://northwind.example/tessera-one"
  }
}
```

**Template** ([basic-template.idml](/file/idml/basic-template.idml)):

![IDML template of a product launch poster with Carbone tags in text frames, a table and the footer](/img/doc/idml-basic-template.webp)

**Result** ([basic-report.idml](/file/idml/basic-report.idml)):

![Product launch poster generated by Carbone and exported to PDF by InDesign](/img/doc/idml-basic-result.webp)

-   Tags work in table cells, like the battery life, and on parent pages, like the dealer in the footer.
-   A tag can be the URL of a hyperlink (Window > Interactive > Hyperlinks > New Hyperlink), like `{d.dealer.url}` on the website of the dealer.
-   A line break in a value, like `"12 Main St\nSpringfield"`, starts a new line in the same paragraph.
-   Turn off typographer's quotes (Preferences > Type > Use Typographer's Quotes). Formatters need straight quotes, like `:formatD('LL')`.
-   All Caps is safe: InDesign displays `{D.CATEGORY}` in the header, but the file keeps the tag as typed, `{d.category}`.

Learn more about [tags](/documentation/design/substitutions/the-basics.md).

## Loops

A loop repeats paragraphs for each item of an array. Write the `[i]` tags in the paragraphs to repeat, like `{d.products[i].name}`, and an `[i+1]` tag in the paragraph after them: `{d.products[i+1].name}`. Carbone repeats the paragraphs with their styles and removes the `[i+1]` paragraph.

**Data** ([loops-data.json](/file/idml/loops-data.json)):

```json
{
  "season": "Fall 2026",
  "validFrom": "2026-09-01",
  "products": [
    {
      "name": "Tessera One",
      "finish": "Graphite or chalk",
      "sku": "AA-T1-100",
      "price": 349
    },
    {
      "name": "Tessera Mini",
      "finish": "Graphite, chalk or sage",
      "sku": "AA-TM-100",
      "price": 199
    },
    {
      "name": "Tessera Max",
      "finish": "Graphite",
      "sku": "AA-TX-100",
      "price": 899
    },
    {
      "name": "Halo Headphones",
      "finish": "Graphite or sand",
      "sku": "AA-HH-100",
      "price": 449
    },
    {
      "name": "Halo Buds",
      "finish": "Graphite or sand",
      "sku": "AA-HB-100",
      "price": 229
    },
    {
      "name": "Desk Stand",
      "finish": "Brushed aluminum",
      "sku": "AA-DS-100",
      "price": 79
    }
  ]
}
```

**Template** ([loops-template.idml](/file/idml/loops-template.idml)):

![IDML template of a price list with a loop on two paragraphs](/img/doc/idml-loops-template.webp)

**Result** ([loops-report.idml](/file/idml/loops-report.idml)):

![Price list with one entry per product generated by Carbone](/img/doc/idml-loops-result.webp)

-   Give the `[i+1]` paragraph its own paragraph style, used by no paragraph around it, like Loop end. Otherwise an empty paragraph stays under the last item.
-   The paragraph above the loop needs another style than the first repeated paragraph, like Columns here. Otherwise it is repeated too.
-   Loops on table rows are not supported: InDesign ignores the copied rows. Align the columns with tab stops, like the prices.

Learn more about [loops](/documentation/design/repetitions/with-arrays.md).

### Nested loops

A loop can contain another loop. In this catalog, `{d.lines[i].name}` repeats the product lines, and `{d.lines[i].items[i].name}` repeats the products of each line. Write the two `[i+1]` tags in the same Loop end paragraph: `{d.lines[i].items[i+1].name}{d.lines[i+1].name}`.

**Data** ([nested-data.json](/file/idml/nested-data.json)):

```json
{
  "edition": "Fall 2026",
  "intro": "Speakers, headphones and accessories, designed to work together.",
  "lines": [
    {
      "name": "Speakers",
      "items": [
        { "name": "Tessera One", "sku": "AA-T1-100", "price": 349, "text": "Room-filling sound from a speaker the size of a book. 24 hours of battery." },
        { "name": "Tessera Mini", "sku": "AA-TM-100", "price": 199, "text": "The pocket version of Tessera One, with the same tuning and 16 hours of battery." },
        { "name": "Tessera Max", "sku": "AA-TX-100", "price": 899, "text": "Seven drivers and a 6.5-inch woofer for large rooms and outdoor parties." },
        { "name": "Tessera Sub", "sku": "AA-TS-100", "price": 699, "text": "A compact subwoofer that pairs with any Tessera speaker in one tap." }
      ]
    },
    {
      "name": "Headphones",
      "items": [
        { "name": "Halo Headphones", "sku": "AA-HH-100", "price": 449, "text": "Over-ear, with adaptive noise cancellation and 40 hours of battery." },
        { "name": "Halo Buds", "sku": "AA-HB-100", "price": 229, "text": "In-ear, with a charging case that adds 24 hours of listening." },
        { "name": "Halo Sport", "sku": "AA-HS-100", "price": 179, "text": "Sweat-resistant buds with ear hooks that stay in place." }
      ]
    },
    {
      "name": "Accessories",
      "items": [
        { "name": "Desk Stand", "sku": "AA-DS-100", "price": 79, "text": "Brushed aluminum stand that tilts Tessera One towards you." },
        { "name": "Wall Mount", "sku": "AA-WM-100", "price": 49, "text": "Black steel mount for Tessera One and Tessera Mini." },
        { "name": "Travel Case", "sku": "AA-TC-100", "price": 59, "text": "Padded case with room for a speaker and its cable." },
        { "name": "Charging Dock", "sku": "AA-CD-100", "price": 89, "text": "Charges a speaker and a pair of buds at the same time." }
      ]
    }
  ]
}
```

**Template** ([nested-template.idml](/file/idml/nested-template.idml)):

![IDML template of a catalog with a loop of product lines containing a loop of products](/img/doc/idml-nested-template.webp)

**Result** ([nested-report.idml](/file/idml/nested-report.idml)):

![Catalog of two pages generated by Carbone, with one heading per product line](/img/doc/idml-nested-result.webp)

![Catalog of two pages generated by Carbone, with one heading per product line, Page 2](/img/doc/idml-nested-result-page-2.webp)

The template has one page and the report has two: InDesign adds the pages, see [Text flow](#text-flow).

## Text flow

InDesign opens the report with the pages and frames of the template, and flows the text like in any document. Here, one story runs through the four badges, in threaded text frames. The first paragraph of each attendee starts in the next frame (Paragraph panel menu > Keep Options > Start Paragraph: In Next Frame), so each attendee gets a badge.

**Data** ([badges-data.json](/file/idml/badges-data.json)):

```json
{
  "event": { "name": "Acme Design Summit", "city": "Lisbon 2026" },
  "attendees": [
    { "firstName": "Maya", "lastName": "Chen", "company": "Northwind Labs", "role": "Speaker" },
    { "firstName": "Lucas", "lastName": "Martin", "company": "Bluebird Logistics", "role": "Attendee" },
    { "firstName": "Amara", "lastName": "Okafor", "company": "Juniper Health", "role": "Press" },
    { "firstName": "Theo", "lastName": "Laurent", "company": "Acme Corporation", "role": "Staff" }
  ]
}
```

**Template** ([badges-template.idml](/file/idml/badges-template.idml)):

![IDML template of event badges with one story threaded through four text frames](/img/doc/idml-badges-template.webp)

**Result** ([badges-report.idml](/file/idml/badges-report.idml)):

![Four event badges generated by Carbone, one per attendee](/img/doc/idml-badges-result.webp)

-   **New pages**: turn on Smart Text Reflow (Preferences > Type) and put the text in a primary text frame. InDesign adds the pages when it opens the report in a window, like page 2 of the [catalog](#loops-nested-loops).
-   **Frames that grow**: a text frame with auto-sizing (Object > Text Frame Options > Auto-Sizing > Height Only) grows with its text.
-   **Overset text**: text that does not fit stays hidden.

## Conditions

Conditions print a value or remove paragraphs, depending on the data:

-   **Inline conditions** print one value or another: `{d.growth:ifGTE(0):show('+'):elseShow('−')}` prints the sign of the growth, and `{d.record:ifEQ(true):show('· Record year'):elseShow('')}` labels a record year.
-   **Conditional blocks** keep the paragraphs between `:showBegin` and `:showEnd` only when the condition is true. Write the begin tag at the end of the paragraph above the block, and the end tag at the end of its last paragraph. `guidance` is empty, so the Guidance update block is removed.

**Data** ([conditions-data.json](/file/idml/conditions-data.json)):

```json
{
  "year": 2026,
  "previousYear": 2025,
  "yearEnd": "2026-09-30",
  "record": true,
  "revenue": 482.6,
  "growth": 18.4,
  "units": "1.2M",
  "markets": 38,
  "margin": 41.2,
  "dividend": 0.42,
  "payDate": "2027-01-15",
  "guidance": ""
}
```

**Template** ([conditions-template.idml](/file/idml/conditions-template.idml)):

![IDML template of an annual report page with inline conditions and conditional blocks](/img/doc/idml-conditions-template.webp)

**Result** ([conditions-report.idml](/file/idml/conditions-report.idml)):

![Annual report page generated by Carbone, without the Guidance update block](/img/doc/idml-conditions-result.webp)

`:hideBegin` and `:hideEnd` remove a block when the condition is true. Learn more about [inline conditions](/documentation/design/conditions/inline-conditions.md) and [conditional blocks](/documentation/design/conditions/conditional-blocks.md).

## Translations

Write static text in `{t( )}` tags, and add `:t` to translate values from the data, like the session types. Carbone uses the translations of the `lang` of the report. This example uses `"lang": "fr-fr"` and the dictionary of the Translations tab. Dates and amounts follow the language: `:formatD('dddd D MMMM YYYY')` prints jeudi 15 octobre 2026, and `:formatC` prints 290,00 €.

**Data** ([translations-data.json](/file/idml/translations-data.json)):

```json
{
  "event": "Acme Design Summit",
  "city": "Lisbonne",
  "date": "2026-10-15",
  "venue": "Centre des congrès",
  "fee": 290,
  "sessions": [
    { "time": "09:00", "title": "Accueil et café", "type": "Break", "room": "Atrium" },
    { "time": "09:30", "title": "Le design au service du son", "type": "Keynote", "room": "A" },
    { "time": "11:00", "title": "Prototyper un objet connecté en une semaine", "type": "Workshop", "room": "B" },
    { "time": "14:00", "title": "Matériaux durables et production locale", "type": "Panel", "room": "A" },
    { "time": "16:00", "title": "Du croquis à la boutique", "type": "Talk", "room": "C" }
  ]
}
```

**Translations** ([translations-translations.json](/file/idml/translations-translations.json)):

```json
{
  "fr-fr": {
    "Program": "Programme",
    "Room": "Salle",
    "Registration": "Inscription",
    "Includes lunch and the evening reception": "Déjeuner et soirée inclus",
    "Break": "Pause",
    "Keynote": "Conférence plénière",
    "Workshop": "Atelier",
    "Panel": "Table ronde",
    "Talk": "Conférence"
  }
}
```

**Template** ([translations-template.idml](/file/idml/translations-template.idml)):

![IDML template of an event program with translation tags](/img/doc/idml-translations-template.webp)

**Result** ([translations-report.idml](/file/idml/translations-report.idml)):

![Event program translated in French by Carbone](/img/doc/idml-translations-result.webp)

A translation can be longer than the original text: leave room in the frames, or let them grow with auto-sizing. Learn more about [translations](/documentation/design/advanced-features/translations-i18n.md).

## Best practices

-   **Use paragraph and character styles.** A value takes the style of its tag, and loops find the paragraphs to repeat by their styles.
-   **Keep each tag in one style.** Select the whole tag, braces included, before you change its font or color: a tag with two styles prints nothing.
-   **Anchor what must repeat.** An icon or a rule placed inline in the repeated paragraphs, after the first `[i]` tag, repeats with them. A floating frame stays in place.
-   **Install the fonts where the report opens.** An IDML file does not contain its fonts.

## Automate an IDML file

To generate an IDML file from an IDML template with the Carbone API:

1.  **Prepare two files**: an IDML template with Carbone tags, like `{d.name}`, and the JSON data to populate it. You can use the files of the [Loops](#loops) example.
2.  **Generate the IDML file** 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](/documentation/developer/http-api/introduction.md), like this curl command:

```bash
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\": $(cat loops-data.json),
    \"template\": \"$(base64 -i loops-template.idml)\",
    \"lang\": \"en-us\"
  }" \
  --output report.idml
```

> 💡 **Linux users**: replace `base64 -i loops-template.idml` with `base64 -w 0 loops-template.idml`

**Key parameters**

| Parameter | Description |
| --- | --- |
| `template` | Your IDML template, encoded as a Base64 string. |
| `data` | JSON data used to populate the template. |
| `lang` | Language of the report, used by the formatters and translations: `"en-us"` prints `{d.price:formatC(0)}` as $349. |
| `convertTo` | Omit it: an IDML template generates an IDML file. |
| `?download=true` | Query parameter to download the document as a stream. |
| `Authorization` | Header required for the Carbone Cloud API. [Get your API key here.](https://account.carbone.io/) |

For more details, refer to the [Carbone API Documentation](/documentation/developer/http-api/introduction.md).

## Related topics

- [XML templates](/documentation/design/template-formats/xml.md)
- [XLSX templates](/documentation/design/template-formats/xlsx.md)
- [TXT templates](/documentation/design/template-formats/txt.md)
- [SVG templates](/documentation/design/template-formats/svg.md)
- [PPTX templates](/documentation/design/template-formats/pptx.md)
- [PDF templates](/documentation/design/template-formats/pdf.md)
