Template Markdown

Scrivi i template email in markdown e lascia che Tratto generi HTML responsive ed email-safe per te.

I template Tratto esistono in due formati: HTML (format: "html", l'HTML lo fornisci tu) e Markdown (format: "emailmd", scrivi markdown e Tratto lo renderizza in HTML responsive ed email-safe — dark mode, fix per Outlook e parte testuale inclusi).

Con un template Markdown:

  • Il markdown è la fonte di verità — vive nel campo source del template. Il campo html è derivato dal server al salvataggio e non può essere impostato direttamente.
  • Il rendering avviene al salvataggio, non all'invio. L'HTML renderizzato viene fissato sul template (e su ogni versione), quindi la consegna è byte-stabile.
  • Le {{variabili}} attraversano il render intatte e vengono sostituite all'invio, esattamente come nei template HTML — anche dentro gli URL dei link.

Preferisci cliccare che leggere? La dashboard ha un editor markdown completo con preview live — vedi Template markdown nella dashboard.

Sintassi

Tutto quello che segue è markdown standard più poche aggiunte specifiche per le email. Il markdown normale funziona come ti aspetti:

## Un titolo

Testo in **grassetto**, un [link](https://example.com), e una lista:

- Primo elemento
- Secondo elemento

Frontmatter

Un blocco YAML opzionale in testa al documento imposta i metadati dell'email e poche opzioni di documento:

---
preheader: "Il tuo ordine è partito. Traccialo ora"
lang: it
dir: ltr
breaks: false
theme: auto
---

Queste cinque chiavi sono l'elenco completo: Tratto passa al renderer solo queste.

ChiaveCosa fa
preheaderIl testo di anteprima che i client email mostrano accanto all'oggetto nella lista della inbox. È incorporato in modo invisibile nel corpo dell'email.
langLa lingua del documento, scritta nell'attributo lang dell'HTML renderizzato. Un tag di lingua: it, en, pt-BR.
dirLa direzione del testo — ltr (default), rtl o auto. rtl ribalta a destra l'allineamento predefinito.
breakstrue trasforma un a-capo singolo in un'interruzione di riga; false (default) mantiene il markdown standard, dove i paragrafi si separano con una riga vuota.
themelight, dark oppure auto (renderizza chiaro ma si adatta ai lettori con il client email in dark mode). Solo questi tre valori: qualsiasi altro viene ignorato, con un warning. Se manca, il documento usa il tema del workspace, e light se il workspace non ne ha uno.

Qualsiasi altra chiave viene rimossa prima del render e segnalata come warning di render. In particolare:

  • I colori (brand_color, button_color, divider_color, background_color, content_color, …) si impostano nelle impostazioni di marchio del workspace, non nel template — vedi Dare un aspetto diverso a una campagna. Il render applica il marchio del workspace come garanzia, non come impostazione predefinita che un documento può sovrascrivere.
  • font_family e fonts non sono supportate. fonts caricherebbe un foglio di stile remoto dentro la casella del destinatario.
  • dark: (palette scura personalizzata) non è supportata. Per una resa scura usa theme: dark o theme: auto, che usano la palette scura coerente del renderer.

Il markdown che salvi non viene modificato: la riga rimossa resta nel source del template esattamente come l'hai scritta, semplicemente non ha effetto sull'HTML renderizzato. Vedere ancora brand_color: nell'editor non significa quindi che sia stato applicato — te lo dicono i warning.

Direttive

Le direttive sono blocchi delimitati che controllano il layout dell'email:

::: header
# Acme Monthly
:::

## Novità di prodotto

Questo mese abbiamo rilasciato **tre** miglioramenti.

::: callout
Il prezzo early-bird finisce **venerdì**.
:::

::: footer
Acme Inc — Milano
[Disiscriviti]({{unsubscribe_url}})
:::
  • ::: header — una banda sopra l'area del contenuto, tipicamente un logo o il nome del brand. Centrata di default; accetta left / right.
  • ::: callout — un riquadro evidenziato per la parte che non deve sfuggire. Parametri separati da spazio lo regolano: center / left / right per l'allineamento, compact / spacious per il padding — es. ::: callout center compact.
  • ::: footer — testo più piccolo e attenuato in fondo, per note legali, link di disiscrizione e informazioni aziendali.
  • ::: include brand-header: il logo del workspace, in una banda di intestazione. Una riga sola, senza ::: di chiusura.

Il logo del workspace

Caricare un logo non lo fa comparire in tutte le email. Un documento lo mostra solo se lo chiede, con questa riga nel punto in cui deve stare il logo (di solito la prima riga dopo il frontmatter):

::: include brand-header

# Il tuo ordine è partito

Al render la riga diventa una banda ::: header con il logo, e il nome del workspace come testo alternativo. È una scelta voluta: un logo sta bene in una newsletter, non per forza nel reset di una password, e per toglierlo da un template basta cancellare la riga.

Il logo viene disegnato dentro un riquadro di 160 × 64 px: rimpicciolito per entrarci, mantenendo le proporzioni, e mai ingrandito. Un logo più piccolo del riquadro resta alla sua dimensione. Le regole di caricamento e gli avvisi sui file piccoli o pesanti sono in Branding.

Se il workspace non ha un logo, la riga non produce niente e il render segnala il warning Unknown partial "brand-header". Il resto dell'email non cambia.

Bottoni

Trasforma qualsiasi link in un bottone call-to-action con l'attributo {button}:

[Conferma email](https://app.example.com/confirm?token={{token}}){button}

[Scopri di più](https://example.com){button.secondary}

Variabili

Usa i placeholder {{nomeVariabile}} ovunque — titoli, prosa, URL dei link. Sopravvivono intatti al render markdown e vengono sostituiti quando l'email viene inviata, con le variables che passi a POST /v1/emails o a un invio di test. Per questo l'HTML del template salvato contiene ancora i placeholder letterali: è voluto.

Dare un aspetto diverso a una campagna

Tema, colori e logo sono un'impostazione di workspace, condivisa da tutti i template. Un documento può solo scegliere il proprio theme e decidere se mostrare il logo.

  • Marchio del workspace: un tema predefinito, i tre colori di marchio e il logo si impostano in Settings → Workspace nella dashboard, oppure via PATCH /v1/workspace (l'oggetto brand) e PUT /v1/workspace/brand/logo (dettagli API in Branding). I colori finiscono qui:

    Campo del marchioDove si vede nell'email
    primaryColorLink, e sfondo dei bottoni primari ({button})
    secondaryColorBordo ed etichetta dei bottoni secondari ({button.secondary})
    accentColorDivisori e linee orizzontali
    themeLa palette (light, dark, auto) dei documenti che non impostano un proprio theme
  • Per singolo documento: theme: light, theme: dark o theme: auto nel frontmatter prevale sul tema del workspace, e ::: include brand-header mostra il logo. Non c'è altro.

Il marchio si applica quando il markdown viene renderizzato, non quando l'email parte. Una modifica al marchio raggiunge ogni render successivo, ma non l'HTML già fissato su template e campagne salvati prima. Un template viene renderizzato di nuovo solo se il suo markdown cambia: salvare lo stesso markdown non applica un logo o dei colori nuovi. Modifica il markdown (anche un solo carattere) e salva, oppure controlla prima il risultato con render-preview, che usa sempre il marchio attuale.

Non esiste un override di colori, font o palette per singolo template, ed è voluto: quello che il workspace ha configurato è quello che ricevono i destinatari.

Sicurezza: l'HTML raw viene escapato

I template markdown vengono renderizzati con l'input HTML disabilitato. Qualsiasi cosa somigli a HTML raw nel markdown (<script>, <img onerror=…>, qualunque tag) viene escapata e mostrata come testo letterale, e i link javascript: / data: non diventano mai link. Se ti serve HTML scritto a mano, crea un template con format: "html" — i due formati non si possono mescolare in un unico template.

End-to-end: markdown → template → test → campagna

1. Crea il template

Invia format: "emailmd" e un campo markdown. Non inviare html — per questo formato è derivato, e fornirlo fa fallire la validazione (422).

curl -X POST https://api.tratto.email/v1/templates \
  -H "Authorization: Bearer tratto_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Email di Benvenuto",
    "format": "emailmd",
    "markdown": "---\npreheader: Il tuo account è pronto\n---\n\n# Benvenuto, {{firstName}}!\n\nGrazie per esserti registrato.\n\n[Conferma email](https://app.example.com/confirm?token={{token}}){button}"
  }'

Risposta (201):

{
  "data": {
    "id": "tmpl_abc123",
    "name": "Email di Benvenuto",
    "status": "draft",
    "format": "emailmd",
    "source": "---\npreheader: Il tuo account è pronto\n---\n\n# Benvenuto, {{firstName}}!...",
    "html": "<!doctype html><html>...</html>",
    "renderWarnings": [],
    "version": 1,
    "createdAt": "2026-08-17T12:00:00Z",
    "updatedAt": "2026-08-17T12:00:00Z"
  }
}

html è il documento renderizzato completo, pronto per l'invio. Il render produce anche una parte in testo semplice, consegnata come alternativa text/plain dell'email. Il sorgente markdown ha un limite di 100.000 caratteri.

2. Preview senza salvare

POST /v1/templates/render-preview renderizza il markdown in modo stateless — è ciò che alimenta la preview live della dashboard:

curl -X POST https://api.tratto.email/v1/templates/render-preview \
  -H "Authorization: Bearer tratto_live_..." \
  -H "Content-Type: application/json" \
  -d '{"markdown": "# Ciao {{firstName}}"}'

Risposta (200): { "data": { "html": "...", "text": "...", "warnings": [] } }

Richiede lo stesso permesso templates:write del salvataggio, e ha un rate limit più stretto di 60 richieste al minuto (il render è CPU-bound), che si somma al limite globale di 500/minuto per IP invece di sostituirlo — vedi Rate Limit.

3. Itera: l'aggiornamento ri-renderizza

PATCH con un nuovo markdown ri-renderizza e crea una nuova versione. Inviare html a un template Markdown — o markdown a un template html — fallisce con 422. Il formato è fissato alla creazione e non può mai essere cambiato.

curl -X PATCH https://api.tratto.email/v1/templates/tmpl_abc123 \
  -H "Authorization: Bearer tratto_live_..." \
  -H "Content-Type: application/json" \
  -d '{"markdown": "# Bentornato, {{firstName}}!"}'

4. Test, pubblica, invia

Da qui in poi un template Markdown si comporta esattamente come uno HTML. L'ordine conta: l'invio di test funziona su una bozza, ma l'invio transazionale finale funziona solo quando il template è published (vedi Template).

# Invio di test alla tua inbox, con valori di esempio per le variabili
curl -X POST https://api.tratto.email/v1/templates/tmpl_abc123/test-send \
  -H "Authorization: Bearer tratto_live_..." \
  -H "Content-Type: application/json" \
  -d '{"to": "tu@example.com", "variables": {"firstName": "Alice", "token": "TEST"}}'

# Pubblica
curl -X PATCH https://api.tratto.email/v1/templates/tmpl_abc123 \
  -H "Authorization: Bearer tratto_live_..." \
  -H "Content-Type: application/json" \
  -d '{"status": "published"}'

# Invia un'email transazionale con il template
curl -X POST https://api.tratto.email/v1/emails \
  -H "Authorization: Bearer tratto_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "from": "hello@tuodominio.it",
    "to": "user@example.com",
    "subject": "Benvenuto!",
    "templateId": "tmpl_abc123",
    "variables": {"firstName": "Alice", "token": "abc123"}
  }'

Anche le campagne partono da un template pubblicato: vedi la sezione seguente.

Modificare il markdown dentro una campagna

Una campagna creata da un template Markdown possiede la propria copia del markdown. Da quel momento il template è il punto di partenza della campagna, non un legame vivo:

  • Alla creazione il contenuto viene copiato, non renderizzato di nuovo. La campagna riceve il source del template e l'HTML fissato su di esso, esattamente come li hai visti nell'editor del template.
  • Modificare il template dopo non cambia la campagna, e modificare la campagna non tocca mai il template.
  • Per cambiare il testo, modifica la campagna. PATCH /v1/campaigns/:id con un campo markdown (oppure l'editor markdown nella pagina della campagna in dashboard) renderizza di nuovo l'HTML della campagna con il marchio attuale del workspace.
curl -X PATCH https://api.tratto.email/v1/campaigns/camp_xyz789 \
  -H "Authorization: Bearer tratto_live_..." \
  -H "Content-Type: application/json" \
  -d '{"markdown": "::: include brand-header\n\n# Novità di ottobre\n\nTre cose che abbiamo rilasciato."}'

Valgono le stesse regole dei template:

RichiestaRisultato
markdown su una campagna Markdown in draftRenderizzata di nuovo e salvata; i warning in renderWarnings della campagna
html su una campagna Markdown, o markdown su una HTML422 VALIDATION_ERROR
html e markdown insieme422 VALIDATION_ERROR
Qualsiasi modifica a una campagna non più in draft409 CONFLICT
templateIdSostituisce il contenuto con una copia nuova di quel template (che deve essere pubblicato). È l'unico modo per cambiare il formato di una campagna

Alla consegna, click tracking, pixel di apertura e footer di disiscrizione vengono applicati esattamente come per le campagne HTML.

Inviare markdown senza template

POST /v1/emails accetta anche un corpo markdown una tantum — nessun template coinvolto. Vedi Invia Email.

Warning di render

Il render non fallisce mai su input imperfetto — i problemi degradano in modo controllato e vengono riportati come warning. Su un template salvato finiscono in renderWarnings; su render-preview tornano in warnings. Trattali come una checklist, non come errori: il template è stato salvato e si invia comunque.

Quelli che incontrerai più spesso:

  • Una chiave frontmatter che non è una delle cinque sopra — è stata rimossa prima del render; il warning nomina la chiave. Le chiavi di colore e di font finiscono sempre qui.
  • Un valore non valido su una chiave supportata — un theme sconosciuto, un dir diverso da ltr / rtl / auto, un lang malformato, un breaks che non è un booleano. Il renderer ricade sul default e lo dice.
  • Un parametro di direttiva sconosciuto — per esempio una parola su una riga ::: callout che non è fra quelle documentate.
  • Unknown partial "brand-header": il documento include il logo ma il workspace non ne ha caricato uno. Al suo posto non compare niente.

Solo due situazioni rifiutano la richiesta, entrambe con VALIDATION_ERROR (422):

  • Markdown vuoto — "markdown must not be empty."
  • Campi in conflitto — markdown insieme a html, sui template o su POST /v1/emails.

Vedi Codici di Errore per i payload.

Con gli SDK

Il supporto markdown richiede la 1.1.0 o successiva degli SDK, su npm come su PyPI, quindi un'installazione nuova ce l'ha; con un pin più vecchio usa direttamente la REST API come mostrato sopra.

Node.js (@tratto/email, 1.1.0+)

// Email markdown una tantum — senza template
const email = await client.emails.send({
  from: 'hello@tuodominio.it',
  to: 'user@example.com',
  subject: 'Benvenuto!',
  markdown: '# Benvenuto, {{firstName}}!',
  variables: { firstName: 'Alice' },
});

// Crea un template Markdown
const template = await client.templates.create({
  name: 'Email di Benvenuto',
  markdown: '# Benvenuto, {{firstName}}!',
});
console.log(template.format);          // 'emailmd'
console.log(template.renderWarnings);  // []

Python (tratto-email, 1.1.0+)

from tratto import SendEmailOptions, CreateTemplateOptions

# Email markdown una tantum — senza template
client.emails.send(SendEmailOptions(
    from_='hello@tuodominio.it',
    to='user@example.com',
    subject='Benvenuto!',
    markdown='# Benvenuto, {{firstName}}!',
    variables={'firstName': 'Alice'},
))

# Crea un template Markdown
template = client.templates.create(CreateTemplateOptions(
    name='Email di Benvenuto',
    markdown='# Benvenuto, {{firstName}}!',
))

PHP (tratto/email, 1.0.0+)

// Email markdown una tantum, senza template
$tratto->emails->send([
    'from' => 'hello@tuodominio.it',
    'to' => 'user@example.com',
    'subject' => 'Benvenuto!',
    'markdown' => '# Benvenuto, {{firstName}}!',
    'variables' => ['firstName' => 'Alice'],
]);

// Crea un template Markdown
$template = $tratto->templates->create([
    'name' => 'Email di Benvenuto',
    'markdown' => '# Benvenuto, {{firstName}}!',
]);

Prossimi Passi


Modifica questa pagina su GitHub

Ultimo aggiornamento