Gestire i template da uno script
Tutto quello che fa la pagina Templates della console è anche una chiamata dell'API, tranne la
rinomina, così un'integrazione può configurarsi da sola — caricare un template, portare avanti una
nuova versione, attivarla — senza che nessuno apra la console. Un template si rinomina in console (la
pagina del template, Name): cambia solo il nome che le persone leggono, il templateId
che il tuo codice chiama resta lo stesso. Il file viaggia in base64 dentro il corpo JSON, la stessa forma di
ogni altro endpoint, invece che in multipart: più comodo per chiamanti come Salesforce Apex, che con il
multipart non hanno vita facile.
| Metodo | Percorso | Cosa fa |
|---|---|---|
| POST | /api/v1/compose/templates | Crea un template e la versione 1, attiva subito. |
| GET | /api/v1/compose/templates | Elenca i template di questa organizzazione. |
| GET | /api/v1/compose/templates/{templateId} | Un template, con ogni versione. |
| POST | /api/v1/compose/templates/{templateId}/versions | Aggiunge una versione. Arriva inattiva — la stessa regola della console. |
| POST | /api/v1/compose/templates/{templateId}/versions/{n}/activate | Attiva la versione n. Le altre restano come sono. |
| POST | /api/v1/compose/templates/{templateId}/versions/{n}/deactivate | Spegne la versione n; le altre continuano a funzionare. |
| POST | /api/v1/compose/templates/{templateId}/deactivate | Spegne tutto il template, con ogni sua versione. |
POST /api/v1/compose/templates
{
"name": "Invoice",
"templateType": "docx",
"fileName": "invoice.docx",
"fileBase64": "UEsDBBQAAAAIA...",
"errorOnMissingFields": false
}
Ognuna di queste chiamate risponde con lo stato attuale completo del template:
{
"templateId": "invoice-a1b2c3",
"name": "Invoice",
"templateType": "docx",
"active": true,
"errorOnMissingFields": false,
"payloadValidation": "warn",
"activeVersions": [1],
"versions": [
{ "versionNumber": 1, "active": true,
"originalFilename": "invoice.docx",
"checksum": "9f8c...", "createdAt": "2026-09-14T10:00:00+00:00",
"hasJsonSchema": false }
]
}
Controllato al caricamento, non alla prima generazione. Questi endpoint controllano il file
subito — lo stesso controllo che fa la console quando vi si carica un file — e rispondono
TEMPLATE_FILE_REJECTED o TEMPLATE_FILE_INVALID nella chiamata di caricamento stessa: un'integrazione
che gira da sola non ha una pagina su cui accorgersi del problema più tardi.
templateType si fissa alla creazione (docx, html o xlsx); ogni
versione successiva di quel template deve avere lo stesso formato. Gli id dei template li assegna il servizio, con
lo stesso schema slug più suffisso dell'admin: non provare a sceglierne uno.
Controllare i dati: JSON Schema
Una versione può portare uno jsonSchema (nel caricamento qui sopra, su una nuova versione, o incollato
in console). Ogni chiamata di composizione che usa quella versione misura il suo data rispetto allo
schema. Cosa succede a una violazione lo decide il payloadValidation del template:
| Modalità | Cosa succede |
|---|---|
warn (predefinita) | Il documento viene prodotto e addebitato come sempre. output.warnings porta PAYLOAD_SCHEMA_VIOLATIONS (2106) con le prime 20 violazioni. |
reject | La chiamata è rifiutata con PAYLOAD_SCHEMA_VIOLATION (1403, 422) prima di prendere credito, elementi di prova o posti di rendering: non costa nulla e non viene mai registrata come risultato di una Idempotency-Key. In un batch bulk l'elemento fallisce con quel codice e non si paga; gli altri proseguono. In un flusso non si compone e non si invia nulla. |
off | Lo schema è ignorato. |
Imposta la modalità con payloadValidation quando crei il template, o su una nuova versione insieme al
suo schema, così un job di CI può pubblicare entrambi in una chiamata e passare a reject:
POST /api/v1/compose/templates/{templateId}/versions
{
"fileName": "invoice.docx",
"fileBase64": "UEsDBBQAAAAIA...",
"jsonSchema": {
"type": "object",
"required": ["cliente"],
"properties": {
"cliente": { "type": "string", "maxLength": 40 },
"importo": { "type": "number", "minimum": 0 }
},
"additionalProperties": false
},
"payloadValidation": "reject"
}
Una violazione è {path, keyword, message}: path è un JSON pointer dentro il tuo
data (/righe/1/qta; vuoto vuol dire tutto il payload), keyword la parola chiave dello
schema che non è stata rispettata, message una frase fatta con le parole dello schema stesso e il
tipo trovato. Il valore che hai inviato non c'è mai, così l'avviso è lo stesso per
un'organizzazione dataless. Anche il nome di una chiave che viene dai tuoi dati (sotto
additionalProperties) lì è nascosto.
Lo schema è input del cliente ed è trattato come un template: è codice. Il servizio applica un
sottoinsieme limitato della draft 2020-12 e rifiuta il resto al caricamento con JSON_SCHEMA_INVALID
(1303), così uno schema o fa quello che dice o non viene accettato.
- Supportati:
type,enum,const,minimum,maximum,exclusiveMinimum,exclusiveMaximum,multipleOf,minLength,maxLength,pattern,minItems,maxItems,uniqueItems,items(un solo schema),required,properties,additionalProperties,minProperties,maxProperties,allOf,anyOf,oneOf,not, e$refverso#o#/$defs/name. - Accettati e ignorati:
$schema,$id,$comment,title,description,default,examples,deprecated,readOnly,writeOnlyeformat(mai verificato). - Rifiutati: tutto il resto (
patternProperties,prefixItems,if/then/else,dependentRequired,unevaluated*), e ogni$refche non sia locale: non si scarica mai nulla. - Limiti: 64 KB di schema, 24 livelli, 1.000 sotto-schemi, 500 valori di enum; un
patterndi al massimo 128 caratteri, senza riferimenti all'indietro né look-around, senza gruppi ripetuti che a loro volta si ripetono, al massimo due ripetizioni illimitate, e una stringa oltre i 2.048 caratteri non viene confrontata con un pattern. Il controllo ha un budget di passi e di tempo; se si esaurisce la risposta è una violazione con keyword$budget, mai "valido".
Ritentare un caricamento
Ogni POST di questa pagina accetta l'header Idempotency-Key, e nessuna ti addebita nulla. Non è
quello il motivo per mandarlo. Un caricamento andato in timeout e ritentato senza chiave ti lascia con
due template con lo stesso nome e due id, e la tua integrazione punta poi a quello tornato per ultimo
mentre qualcuno in console sceglie l'altro.
POST /api/v1/compose/templates
Authorization: Bearer lpdc_…
Idempotency-Key: invoice-template-v3
Le regole sono quelle di comporre un documento, invariate: la stessa
chiave con lo stesso corpo ripete la prima risposta con Idempotent-Replay: true, la stessa chiave con un
corpo diverso è rifiutata, e un tentativo fallito non viene mai registrato, così il successivo gira davvero.