Template: Crea, Versiona e Test

Gestisci template email versionati con sostituzione di variabili

I template sono template email riutilizzabili con variabili placeholder. Invece di comporre HTML nel codice ogni volta, definisci un template una volta e invialo centinaia di volte con variabili diverse.

Due Formati: HTML e Markdown

Ogni template ha un format, scelto alla creazione e immutabile dopo:

  • html (default) — l'HTML dell'email lo fornisci tu. Tutto in questa pagina usa questo formato.
  • emailmd — fornisci markdown nel campo markdown; il server lo renderizza in HTML responsive ed email-safe al salvataggio e memorizza il markdown come source. Il campo html della risposta è l'output renderizzato e non può essere impostato direttamente (inviare html per questo formato fallisce con 422). Un endpoint stateless POST /v1/templates/render-preview renderizza il markdown senza salvare.

I template markdown hanno una guida dedicata — sintassi, modello di sicurezza, flusso end-to-end: Template Markdown. Le risposte in questa pagina omettono il campo format per brevità; i template creati prima dell'esistenza dei formati sono html.

Crea un Template

Un template è un nome più un corpo. La richiesta accetta name, format e uno fra html e markdown — nient'altro.

Un template non ha un oggetto. Non esiste un campo subject sul template, né un campo text che tu possa impostare: l'oggetto appartiene al singolo invio. Passarlo in questa richiesta non ha alcun effetto — viene scartato in silenzio, non memorizzato.

cURL

curl -X POST https://api.tratto.email/v1/templates \
  -H "Authorization: Bearer tratto_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name":"Email di Benvenuto",
    "html":"<h1>Benvenuto, {{firstName}}!</h1><p>Il tuo codice: {{code}}</p>"
  }'

Risposta:

{
  "data": {
    "id": "tmpl_abc123",
    "name": "Email di Benvenuto",
    "html": "<h1>Benvenuto, {{firstName}}!</h1><p>Il tuo codice: {{code}}</p>",
    "status": "draft",
    "version": 1,
    "createdAt": "2025-06-30T12:00:00Z",
    "updatedAt": "2025-06-30T12:00:00Z"
  }
}

Pubblica prima di inviare

Un template nasce con status: "draft", e status accetta draft o published. Solo un template published può essere usato per inviare. Pubblicare è un passaggio, non un'etichetta: il template deve essere published, e qualsiasi altro stato viene rifiutato.

Pubblicalo come qualsiasi altro aggiornamento di campo:

cURL

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

status è anche un filtro sull'endpoint di elenco: GET /v1/templates?status=published.

Dove si applica il gate

Cosa fai con un template in bozzaCosa succede
POST /v1/campaigns con templateId422 TEMPLATE_NOT_PUBLISHED
PATCH /v1/campaigns/:id che sostituisce templateId422 TEMPLATE_NOT_PUBLISHED
POST /v1/flows/:id/activate con uno step send_email che lo usa422 TEMPLATE_NOT_PUBLISHED (404 se il template non esiste)
POST /v1/emails con templateIdaccettata, poi l'email viene marcata failed e non parte
POST /v1/templates/:id/test-sendpermesso, vedi sotto

Gli errori di campagne e flussi hanno questa forma:

{
  "error": {
    "code": "TEMPLATE_NOT_PUBLISHED",
    "message": "Template 'tmpl_abc123' is not published and cannot be used to send.",
    "docs": "https://docs.tratto.email/en/docs/error-codes",
    "suggestion": "Publish the template first: PATCH /v1/templates/:id with { \"status\": \"published\" }, or use the Publish button in the template editor."
  }
}

All'attivazione di un flusso il messaggio nomina anche lo step: Template 'tmpl_abc123' on step 's0' is not published and cannot be used to send.

POST /v1/emails non legge il template quando accetta la richiesta, quindi risponde comunque 202. Il gate viene applicato dal worker di consegna: l'email finisce con status: "failed" e non parte nulla. Con l'API 0.29.0 o successiva GET /v1/emails/{id} restituisce failureReason: "template_not_published", e i webhook iscritti a failed ricevono un evento failed il cui data.error è Template 'tmpl_abc123' is not published and cannot be used to send. L'email resta comunque persa, quindi non inviare con una bozza. Lo stesso vale se un template viene de-pubblicato dopo che un'email è già stata messa in coda con quel template.

I test-send sono esenti. POST /v1/templates/:id/test-send funziona anche su un template draft: controllare una bozza in una casella vera è esattamente il punto, ed è l'unico modo di rivederla prima di pubblicarla. Il test di una campagna non è toccato in nessun caso: invia la copia dell'HTML che la campagna possiede e non risolve mai un template.

Il gate non è retroattivo. Una campagna copia dentro di sé l'HTML del template al momento della creazione, quindi le campagne create prima che questo esistesse mantengono il loro contenuto e continuano a partire. Il gate si applica quando una campagna viene creata o le viene sostituito il template, quando un flusso viene attivato, e in consegna per le email che risolvono un template.

Invia Usando un Template

Il template fornisce il corpo e deve essere published (vedi Pubblica prima di inviare). subject è obbligatorio sull'invio e va passato ogni volta: se lo ometti la richiesta fallisce con 422.

cURL

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":"Benvenuta, {{firstName}}!",
    "templateId":"tmpl_abc123",
    "variables":{
      "firstName":"Alice",
      "code":"SECRET123"
    }
  }'

Risultato:

Subject: Benvenuta, Alice!
HTML: <h1>Benvenuto, Alice!</h1><p>Il tuo codice: SECRET123</p>

L'oggetto viene renderizzato con le stesse variables del corpo, quindi {{firstName}} funziona in entrambi. Una variabile che non passi diventa una stringa vuota anche nell'oggetto — vedi Passa Sempre Tutte le Variabili più sotto.

Un template html non ha una parte testuale. Un template Markdown fissa al salvataggio una parte testuale renderizzata, e quella viene inviata come text/plain accanto all'HTML.

Aggiorna un Template

Aggiorna un template. Questo crea una nuova versione, lasciando intatte le versioni precedenti.

cURL

curl -X PATCH https://api.tratto.email/v1/templates/tmpl_abc123 \
  -H "Authorization: Bearer tratto_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "html":"<h1>Benvenuto di nuovo, {{firstName}}!</h1><p>Codice: {{code}}</p>"
  }'

Risposta: il template completo, con version incrementata.

{
  "data": {
    "id": "tmpl_abc123",
    "name": "Email di Benvenuto",
    "html": "<h1>Benvenuto di nuovo, {{firstName}}!</h1><p>Codice: {{code}}</p>",
    "status": "draft",
    "version": 2,
    "createdAt": "2025-06-30T12:00:00Z",
    "updatedAt": "2025-06-30T12:05:00Z"
  }
}

La versione si incrementa solo se il contenuto cambia davvero: una PATCH che reimposta lo stesso HTML la lascia com'è. Le versioni non hanno uno status proprio, e il bump di versione non tocca lo status del template.

Per i template Markdown, aggiorna invece il campo markdown — ogni modifica ri-renderizza l'HTML e incrementa la versione allo stesso modo. html e markdown sono mutuamente esclusivi in una PATCH, e ciascuno è accettato solo da un template del formato corrispondente (422 altrimenti).

De-pubblicare un template usato da un flusso attivo

Riportare status a draft risponde 409 se un flusso attivo sta usando quel template:

{
  "error": {
    "code": "CONFLICT",
    "message": "This template is used by flow 'Benvenuto' (active). Unpublishing it would break the next send.",
    "suggestion": "Pause the flow, or point that step at another template first."
  }
}

Il motivo è il gate descritto sopra: un template non pubblicato viene rifiutato in consegna, quindi de-pubblicarlo mentre un flusso lo usa lo romperebbe al prossimo step. Un flusso in bozza non blocca nulla, perché non spedisce.

Cronologia delle Versioni

Elenca le versioni di un template, dalla più recente (fino a 20). Ogni voce contiene solo il numero di versione e il momento del salvataggio:

cURL

curl https://api.tratto.email/v1/templates/tmpl_abc123/versions \
  -H "Authorization: Bearer tratto_live_..."

Risposta:

{
  "data": [
    { "version": 2, "savedAt": "2025-06-30T12:05:00Z" },
    { "version": 1, "savedAt": "2025-06-30T12:00:00Z" }
  ]
}

Ottieni una Versione

Per leggere il contenuto di una versione passata — l'unico modo di recuperarlo — richiedila per numero:

cURL

curl https://api.tratto.email/v1/templates/tmpl_abc123/versions/1 \
  -H "Authorization: Bearer tratto_live_..."

Risposta:

{
  "data": {
    "version": 1,
    "html": "<h1>Benvenuto, {{firstName}}!</h1><p>Il tuo codice: {{code}}</p>",
    "savedAt": "2025-06-30T12:00:00Z"
  }
}

Su un template Markdown la risposta porta anche source: il markdown che ha prodotto l'HTML di quella versione.

Invio di Test

Invia un'email di test usando il template per verificare che appaia corretta.

cURL

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":"your-email@example.com",
    "variables":{
      "firstName":"Alice",
      "code":"TEST123"
    }
  }'

Un'email di test viene inviata immediatamente al tuo indirizzo in modo che tu possa esaminarla nella tua inbox.

Se Tratto non riesce a mettere in coda l'email di test, l'API restituisce 503 SERVICE_UNAVAILABLE con l'header Retry-After: non è partito niente e non è stata creata alcuna email, quindi puoi riprovare. Vedi Codici di Errore.

Il suo oggetto è [Test] seguito dal nome del template. Un invio di test non ha un contatto, quindi le variabili del destinatario che non passi prendono valori di anteprima: email è l'indirizzo di test, firstName è Preview, lastName è Recipient e unsubscribe_url è un link di anteprima che non disiscrive nessuno. Ogni altra variabile che ometti diventa vuota, come in un invio reale.

Gratis, con un tetto giornaliero

I test-send sono gratuiti: non contano sulla quota mensile. Ogni chiamata invia a un solo destinatario. Con l'API 0.24.0 o successiva, un workspace può fare 20 test-send al giorno su Free, 100 su Starter e Growth e 200 su Scale, fra template e campagne insieme, e il conteggio si azzera a mezzanotte UTC. Oltre il tetto l'API risponde 429 QUOTA_EXCEEDED e non parte niente: non riprovare prima dell'azzeramento. Una chiamata rifiutata con 404 o 422 non consuma il tetto; una chiamata accettata sì, anche se poi risponde 503, perché il tetto si conta prima della consegna alla coda.

{
  "error": {
    "code": "QUOTA_EXCEEDED",
    "message": "Test send daily limit of 20 per workspace reached.",
    "suggestion": "The limit resets at midnight UTC. Test sends are free and do not count against your monthly quota. Starter raises the test-send limit to 100 a day. The account owner can upgrade at https://app.tratto.email/settings/billing.",
    "docs": "https://docs.tratto.email/en/docs/error-codes"
  }
}

Elenca i Template

Elenca i template del workspace, dal più recente, una pagina alla volta. Richiede templates:read.

cURL

curl "https://api.tratto.email/v1/templates?status=published&limit=50" \
  -H "Authorization: Bearer tratto_live_..."

Parametri di query:

ParametroDefaultA cosa serve
limit50Risultati per pagina, da 1 a 100
afternessunoIl nextCursor della pagina precedente
statusnessunodraft o published. Qualsiasi altro valore risponde 422
includenessunohtml aggiunge a ogni riga l'html del template. È l'unico valore accettato

Risposta:

{
  "data": [
    {
      "id": "tmpl_abc123",
      "name": "Benvenuto",
      "status": "published",
      "format": "html",
      "version": 2,
      "createdAt": "2025-06-30T12:00:00Z",
      "updatedAt": "2025-07-02T09:30:00Z"
    }
  ],
  "pagination": {
    "hasMore": true,
    "nextCursor": "tmpl_abc123"
  }
}

Le righe non contengono html se non lo chiedi con include=html: è il campo più pesante di un template e a quasi tutti gli elenchi non serve. Per tutto il resto di un singolo template usa Ottieni un Template.

L'elenco si ferma a una pagina. Senza after ricevi i 50 template più recenti, e l'unico segnale che ce ne sono altri è pagination.hasMore. Per leggerli tutti passa nextCursor come after finché hasMore è false:

# Prima pagina
curl "https://api.tratto.email/v1/templates?limit=100" \
  -H "Authorization: Bearer tratto_live_..."

# Pagina successiva: after = il nextCursor appena ricevuto
curl "https://api.tratto.email/v1/templates?limit=100&after=tmpl_abc123" \
  -H "Authorization: Bearer tratto_live_..."

Sull'ultima pagina hasMore è false e nextCursor è null. Un cursore che non corrisponde a un template esistente viene ignorato e l'elenco riparte dalla prima pagina. Tieni lo stesso status su tutte le pagine di una stessa lettura.

Ottieni un Template

Recupera un template specifico.

cURL

curl https://api.tratto.email/v1/templates/tmpl_abc123 \
  -H "Authorization: Bearer tratto_live_..."

Elimina un Template

Elimina permanentemente un template (non può essere annullato).

cURL

curl -X DELETE https://api.tratto.email/v1/templates/tmpl_abc123 \
  -H "Authorization: Bearer tratto_live_..."

L'eliminazione di un template non può essere annullata: se ne va anche tutta la cronologia delle versioni, con l'HTML e il markdown di ogni revisione passata.

Un template in uso non si elimina. Se un flusso lo referenzia, o se una campagna non ancora conclusa (scheduled, sending, paused) lo usa, la richiesta risponde 409 e dice chi lo sta usando:

{
  "error": {
    "code": "CONFLICT",
    "message": "This template is used by flow 'Benvenuto' (active).",
    "suggestion": "Remove it from those first, or leave the template in place."
  }
}

Una campagna già completed non blocca l'eliminazione: non spedirà più.

Prima del 29 settembre 2026 l'eliminazione passava comunque, e il flusso falliva al primo invio successivo: l'email veniva segnata failed e l'iscrizione avanzava lo stesso, quindi il destinatario non riceveva nulla e in dashboard non compariva alcun errore.

Sintassi Variabile del Template

Usa le doppie graffe {{variableName}} per i placeholder — gli spazi attorno sono ammessi, quindi funziona anche {{ firstName }}. Le variabili fanno distinzione tra maiuscole e minuscole. Non ci sono condizioni né cicli: è sostituzione, non un linguaggio di template.

Template:

HTML: <p>Ciao {{firstName}}, la tua email è {{email}}</p>

Invio:

{
  "variables": {
    "firstName": "Alice",
    "email": "alice@example.com"
  }
}

Renderizzato:

HTML: <p>Ciao Alice, la tua email è alice@example.com</p>

La sostituzione si applica all'oggetto e ai corpi HTML e testo.

Best Practice

1. Usa Nomi Variabili Semantici

✅ {{firstName}}, {{orderID}}, {{confirmationCode}}
❌ {{x}}, {{var1}}, {{v}}

2. Passa Sempre Tutte le Variabili

Una variabile mancante non fa fallire l'invio: il placeholder viene sostituito con una stringa vuota. Questo template

<p>Ciao {{firstName}}, benvenuto a {{companyName}}!</p>

inviato senza firstName né companyName arriva come Ciao , benvenuto a ! — la punteggiatura resta orfana, ma il destinatario non vede mai il nome della tua variabile.

Vale per l'oggetto e per il corpo, in HTML e in Markdown, sia sugli invii transazionali che sulle campagne. Anche null sostituisce vuoto: non comparirà mai la stringa null o undefined in un'email.

Non è un invito a passare meno variabili — al contrario. Una frase con un buco resta una frase sbagliata, e ora niente te lo segnala: né all'invio, né prima. Con il placeholder visibile un refuso come {{firstname}} al posto di {{firstName}} almeno si notava nell'anteprima; con la sostituzione vuota sparisce senza lasciare traccia.

Il controllo automatico esiste solo per le campagne, dove Tratto conosce l'insieme delle variabili disponibili e può quindi segnalare in bozza quelle che non ne fanno parte. L'insieme è elencato in Variabili di una Campagna. Per un template non è possibile: le variabili arrivano da chi chiama POST /v1/emails, quindi non esiste un insieme contro cui verificarle e ogni controllo sarebbe un'ipotesi.

Su un template, quindi, l'unica difesa è passare a ogni invio tutti i placeholder che il template usa.

3. Usa Template per Invii Bulk

Non comporre HTML per ogni email. Usa template e variabili per consistenza e riutilizzabilità.

4. Fai un Test Prima di Usarlo

Fai sempre un test-send del template prima di inviarlo a qualcuno, per intercettare problemi di rendering. Il test-send è l'unico invio che un template draft può fare, quindi è anche l'unica revisione possibile prima di pubblicarlo.


Prossimi Passi


Modifica questa pagina su GitHub

Ultimo aggiornamento