Riferimento Codici di Errore
I codici di errore restituiti dall'API Tratto, e cosa fare per ciascuno.
L'API restituisce un piccolo insieme di codici di errore. Sono volutamente generici: il codice
indica la classe del problema e lo stato HTTP, mentre message contiene il
dettaglio specifico.
Fai i controlli su code, non su message: i messaggi sono scritti per le
persone e possono cambiare senza preavviso.
Formato degli Errori
{
"error": {
"code": "NOT_FOUND",
"message": "Webhook 'wh_abc123' not found.",
"docs": "https://docs.tratto.email/en/docs/error-codes"
}
}suggestion è presente su alcuni errori con un'indicazione su come risolvere.
Gli errori di validazione aggiungono un array details: vedi sotto.
I Codici
| Codice | HTTP | Significato | Cosa fare |
|---|---|---|---|
UNAUTHORIZED | 401 | Credenziali mancanti o non valide (una chiave API, o il token della sessione della dashboard), oppure una sessione della dashboard iniziata prima di una revoca delle sessioni dell'account (Sign in again.) | Controlla l'header Authorization: Bearer; nella dashboard, accedi di nuovo |
FORBIDDEN | 403 | La richiesta non è consentita: alla chiave manca un permesso, il dominio mittente non è verificato, o l'azione è bloccata per questa risorsa | Leggi message e suggestion; se manca un permesso, usa una chiave che lo abbia |
NO_WORKSPACE | 403 | L'account ha effettuato l'accesso ma non ha un workspace | Crea il workspace dalla dashboard, oppure accedi con l'account che appartiene al tuo workspace; vedi sotto |
TENANT_SUSPENDED | 403 | Il workspace è sospeso: è in sola lettura, quindi non può inviare né modificare nulla. Arriva anche alla creazione di un workspace (POST /v1/workspaces) quando uno dei workspace posseduti dall'account è sospeso (Your account has a suspended workspace: a new one cannot be created.) | Rispondi all'avviso di sospensione inviato al proprietario del workspace, oppure scrivi a support@tratto.email |
TENANT_CLOSED | 403 | Il workspace è stato eliminato: le sue chiavi API vengono rifiutate, così come una sessione della dashboard il cui header Tratto-Workspace lo indica | Non c'è nulla da riprovare. Vedi Workspace |
NOT_A_MEMBER | 403 | Una sessione della dashboard opera su un workspace a cui l'account non ha accesso: l'account ne è stato rimosso, oppure l'header Tratto-Workspace indica un workspace di cui l'account non è proprietario né membro attivo | Accedi di nuovo per tornare al tuo workspace. Con l'header, invia l'id di un workspace a cui l'account ha accesso, oppure omettilo per usare quello predefinito. Vedi Autenticazione |
REAUTH_REQUIRED | 401 | L'azione richiede un accesso più recente: degli ultimi 5 minuti per eliminare il workspace, o successivo all'ultima revoca delle sessioni dell'account per accettare un invito | Accedi di nuovo dalla dashboard, poi ripeti l'azione |
MEMBER_INVITES_RESTRICTED | 403 | Gli inviti ai membri non sono disponibili per questo workspace: vengono aperti gradualmente | Scrivi a support@tratto.email. Vedi Team |
PLAN_UPGRADE_REQUIRED | 403 | Il piano del workspace non include la funzionalità. Sul Free: creare un webhook, inviare una consegna di prova o ruotarne il segreto (richiede l'API 0.23.0 o successiva); caricare un allegato o inviare un'email con attachments (richiede l'API 0.29.0 o successiva) | Il proprietario dell'account passa a un piano superiore dalla dashboard in Impostazioni > Fatturazione (/settings/billing). Vedi Webhook e Allegati |
EMAIL_NOT_VERIFIED | 403 | L'azione richiede un indirizzo email verificato sull'account (eliminazione del workspace) | Verifica l'indirizzo: la dashboard offre l'invio dell'email di verifica |
NOT_FOUND | 404 | La risorsa non esiste | Verifica l'ID: potrebbe appartenere a un altro workspace |
CONFLICT | 409 | La risorsa esiste già, oppure il suo stato attuale non consente l'azione (ad esempio modificare una campagna che non è più in bozza, eliminare un template usato da un flusso o da una campagna non conclusa, o accettare un invito scaduto; i casi dei template sono in Template, i casi degli inviti sono elencati in Team). Su POST /v1/workspaces: un account che possiede già 10 workspace contando quelli in eliminazione (vedi Workspace) | Recupera la risorsa e verificane lo stato prima di riprovare. Per la creazione di un workspace, scrivi a support@tratto.email |
VALIDATION_ERROR | 422 | Il corpo è stato letto ma non rispetta lo schema, oppure l'header Tratto-Workspace non è un id di workspace valido | Per il corpo, leggi details per i campi errati. Per l'header non c'è details: message è The Tratto-Workspace header is not a workspace id.; invia l'id di un workspace a cui l'account ha accesso, oppure ometti l'header. Vedi Autenticazione |
TEMPLATE_NOT_PUBLISHED | 422 | Il template indicato nella richiesta è ancora in bozza e non può essere usato per inviare | Pubblicalo (PATCH /v1/templates/:id con status: "published"), poi ripeti la richiesta |
BAD_REQUEST | 400 | La richiesta non è arrivata a un handler: il corpo non è interpretabile (JSON malformato) | Correggi la sintassi del corpo: il payload non è mai arrivato allo schema |
UNSUPPORTED_MEDIA_TYPE | 415 | Il Content-Type non è fra quelli che l'endpoint sa leggere | Usa il tipo atteso (application/json nella maggior parte dei casi, text/csv per l'import contatti, image/png o image/jpeg per il logo del marchio) |
INVALID_TOKEN | 400 | Un link firmato (disiscrizione, pagina preferenze) non è valido | Non c'è nulla da riprovare: serve un link nuovo |
RATE_LIMITED | 429 | Troppe richieste: 100 al secondo per chiave API, 500 al minuto per IP client, 1 caricamento di allegato al secondo per chiave API, o troppe richieste simultanee sullo stesso account | Attendi e riprova, rispettando Retry-After se la risposta lo imposta |
PAYLOAD_TOO_LARGE | 413 | Il corpo della richiesta supera la dimensione massima. Per POST /v1/attachments, un file oltre 25 MB | Spezza il payload (per l'import contatti, carica meno righe per richiesta). Per un allegato, invia invece un link al file |
QUOTA_EXCEEDED | 429 | Raggiunto un limite d'uso: la quota mensile di email (su POST /v1/emails quando l'email ha più destinatari distinti fra to, cc e bcc delle email che restano all'account, dove ogni destinatario conta come un'email, o inviando subito una campagna a più destinatari delle email che restano nella quota), il limite di domini (quando aggiungi un dominio), le generazioni AI mensili nella dashboard, il tetto giornaliero del test mode di 100 email di test, il tetto giornaliero di test-send per workspace (20 su Free, 100 su Starter e Growth, 200 su Scale, sui test-send di template e campagne, con l'API 0.24.0 o successiva), il limite di membri del team dell'account (all'invito o all'accettazione di un invito, inviti in attesa compresi, con l'API 0.28.0 o successiva), il numero di workspace che l'account può possedere (alla creazione di un workspace), o lo spazio per gli allegati del workspace, 1 GB (al caricamento di un allegato, con l'API 0.29.0 o successiva). La quota di email, il limite di domini, le generazioni AI, il limite di workspace e il limite di membri del team appartengono all'account e sono condivisi dai suoi workspace | Non riprovare: message indica il limite. La quota di email si azzera all'inizio del mese UTC successivo, il tetto del test mode e quello dei test-send a mezzanotte UTC. Per il limite di domini elimina un dominio o passa a un piano superiore; per la quota di email passa a un piano superiore; per il limite di persone revoca un invito in attesa, rimuovi un membro o passa a un piano superiore (vedi Team); per il limite di workspace elimina un workspace o passa a un piano superiore (vedi Workspace); per lo spazio degli allegati elimina quelli che non ti servono più o aspetta che alcuni scadano (vedi Allegati) |
TEST_MODE_NOT_SUPPORTED | 403 | L'endpoint raggiunge destinatari reali e rifiuta le chiavi di test | Usa una chiave live (tratto_live_...) — vedi Test mode |
IDEMPOTENCY_CONFLICT | 409 | La stessa Idempotency-Key è stata riusata con un payload diverso | Usa una chiave nuova per una richiesta diversa |
IDEMPOTENCY_IN_PROGRESS | 409 | Una richiesta con questa Idempotency-Key è ancora in elaborazione | Attendi che la prima finisca, poi riprova |
INTERNAL_ERROR | 500 | Errore dalla nostra parte | Riprova con backoff; contatta il supporto se persiste |
SERVICE_UNAVAILABLE | 503 | Un servizio da cui dipende l'endpoint è disattivato, saturo o non risponde | Riprova più tardi, rispettando Retry-After se la risposta lo imposta; suggestion indica se attendere serve. Sui test-send di campagne e template, un 503 significa che l'email non è stata messa in coda: non è stato creato, inviato o addebitato niente, quindi riprovare è sicuro. Su POST /v1/emails un 503 significa che non è stato possibile salvare l'email o consegnarla alla coda: non è partito niente, e il messaggio dice se c'è stato un addebito (Nothing was sent and nothing was charged. oppure, con l'API 0.21.0 o successiva quando non è stato possibile restituire la quota, Nothing was sent. This request may still count against the monthly quota.). Riprova dopo Retry-After, riusando la stessa Idempotency-Key se l'hai inviata |
Una risorsa inesistente non ha un codice tutto suo. Un template inesistente, un
contatto inesistente e una campagna inesistente restituiscono tutti
NOT_FOUND: la risorsa è indicata in message, non in code.
BAD_REQUEST (400) e VALIDATION_ERROR (422)
Entrambi significano «la tua richiesta è sbagliata», ma falliscono in due momenti diversi:
BAD_REQUEST(400) — la richiesta non è mai arrivata a un handler. Il corpo non è stato interpretabile: JSON malformato, payload troncato, unContent-Typeche dichiara JSON su qualcosa che non lo è. Non c'è arraydetails, perché nessuno schema è stato eseguito.VALIDATION_ERROR(422) — il corpo è stato letto e poi ha fallito lo schema. È questo a dirti quale campo è sbagliato.
Cambiato il 2026-09-04. Tre casi rispondevano 500 INTERNAL_ERROR e non lo
fanno più: un corpo vuoto inviato con Content-Type: application/json — la
forma che curl e Postman producono di default per una mutazione senza campi,
come POST /v1/campaigns/{id}/pause, POST /v1/campaigns/{id}/unschedule o
qualunque DELETE — ora va a buon fine normalmente (200/204/409); il JSON
malformato ora risponde 400 BAD_REQUEST; un Content-Type non gestito
ora risponde 415 UNSUPPORTED_MEDIA_TYPE. Se il tuo client tratta i 5xx come
riprovabili e i 4xx come definitivi, per questi tre casi la classificazione ora
è corretta.
Errori di validazione
VALIDATION_ERROR restituisce 422 e include un array details
prodotto dal validatore dello schema, che identifica ogni campo non valido:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed.",
"docs": "https://docs.tratto.email/en/docs/error-codes",
"details": [
{
"path": "/to",
"message": "Invalid email"
}
]
}
}Con l'API 0.24.0 o successiva, POST /v1/emails e PATCH /v1/emails/{id}
rifiutano anche un campo che non conoscono: la voce ha path / per un campo
al primo livello del body, e un message che lo nomina, come
Unrecognized key: "reply_to". Vedi
Campi sconosciuti.
Validazione Markdown
Il supporto markdown su POST /v1/emails e sui
template aggiunge due casi di VALIDATION_ERROR (422) da conoscere:
markdownehtmlinsieme. I due campi sono mutuamente esclusivi ovunque. SuPOST /v1/emailsil messaggio è"markdown and html are mutually exclusive — provide one or the other."; sui template,htmlviene rifiutato del tutto performat: "emailmd"("html is not accepted when format is 'emailmd' — the HTML is derived from the markdown render.") emarkdownrichiede quel formato.- Markdown vuoto. Un campo
markdownvuoto o di soli spazi fallisce con"markdown must not be empty."— il render non gira mai su input vuoto.
TEMPLATE_NOT_PUBLISHED (422)
Un template deve essere published prima di poter essere usato per inviare.
È un codice a sé, non una variante di VALIDATION_ERROR: non porta l'array
details, perché nessuno schema ha rifiutato il corpo. Lo restituiscono
POST /v1/campaigns, una PATCH /v1/campaigns/:id che sostituisce il template
e POST /v1/flows/:id/activate:
{
"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 flow il messaggio indica anche lo step che usa il
template:
Template 'tmpl_abc123' on step 's0' is not published and cannot be used to send.
Un templateId inesistente è invece un NOT_FOUND (404). Il test-send del
template è esente, e POST /v1/emails non viene rifiutata affatto: accetta la
richiesta e l'email fallisce in consegna. Vedi
Template.
NO_WORKSPACE (403)
Una sessione della dashboard (un account che ha effettuato l'accesso, non una
chiave API) il cui account non ha un workspace riceve questo codice su ogni
rotta autenticata, tranne le due che accettano un invito, quella che crea il
workspace e quella che li elenca: GET /v1/workspaces risponde 200 con
{"data":[]}, con o senza accesso alla beta. Prima viene il
controllo dell'accesso alla beta: un account senza accesso alla beta riceve
quell'errore.
{
"error": {
"code": "NO_WORKSPACE",
"message": "This account has no workspace.",
"docs": "https://docs.tratto.email/en/docs/error-codes"
}
}Non c'è suggestion. Accettare l'invito alla beta non crea un workspace, e
nemmeno GET /v1/workspace. Nella dashboard questo codice apre la pagina
«Questo account non ha un workspace»: il bottone «Crea il tuo workspace» lo crea
e ti porta dentro. Da lì puoi anche accedere con un altro account o riprovare.
Il bottone chiama POST /v1/workspaces, che accetta solo una sessione della
dashboard. Risponde 201 con lo stesso oggetto di GET /v1/workspace. Il campo
name è facoltativo (da 1 a 100 caratteri, predefinito My Workspace): un nome
non valido riceve 422. La stessa rotta crea i workspace oltre il primo, entro
il limite del piano; i rifiuti sono elencati in
Workspace. Gli id dei workspace creati così iniziano con
ws_; i workspace esistenti tengono il loro id.
Le chiavi API non sono coinvolte: una chiave appartiene sempre a un workspace,
e con una chiave non si crea un workspace (POST /v1/workspaces risponde 403).
Due codici condividono il 429
RATE_LIMITED e QUOTA_EXCEEDED restituiscono entrambi 429, ma richiedono
reazioni opposte:
RATE_LIMITED: stai inviando troppo in fretta. Rallenta e riprova: la richiesta andrà a buon fine a breve. Vedi Rate Limit.QUOTA_EXCEEDED: hai raggiunto un limite d'uso, come la quota mensile di destinatari. Riprovare non serve finché il limite non si azzera, non elimini un dominio o un workspace, non liberi un posto nel workspace o non passi a un piano superiore.
La quota mensile di email, il limite di domini e le generazioni AI appartengono
all'account proprietario del workspace, e tutti i workspace di
quell'account vi attingono. Un QUOTA_EXCEEDED può arrivare da un altro
workspace dello stesso account o da un altro membro, e il suo message dice
che il limite è condiviso dall'account. Vedi Rate Limit.
Il lavoro in background non risponde mai con questo codice. Una campagna che il
dispatcher non riesce a far stare nella quota rimasta, prima di partire o a
metà invio, viene messa in pausa con pausedReason: "quota_exceeded" (uno dei
quattro valori elencati in
Campagne); riprenderla più tardi
raggiunge solo i destinatari mancati. Un flusso il cui step di invio trova la quota esaurita
salta quell'email e prosegue: l'email non viene inviata in seguito.
Trattarli allo stesso modo significa riprovare all'infinito contro un muro:
if (error.status === 429) {
if (error.code === 'QUOTA_EXCEEDED') {
// Avvisa qualcuno. Riprovare non può risolvere.
throw error;
}
await backoff();
return retry();
}Gestione degli errori con l'SDK
Entrambi gli SDK sollevano un TrattoError che porta il code e lo status
HTTP, e ognuno espone una parte del resto del payload: l'SDK Node aggiunge
docs, quello Python aggiunge suggestion e docs dalla 1.2.0 o
successiva. Dirama sul codice; leggi suggestion quando un codice copre più
di una situazione.
Node.js
@tratto/email solleva TrattoError, che espone code, statusCode e docs:
import { TrattoError } from '@tratto/email';
try {
await tratto.emails.send({ /* … */ });
} catch (error) {
if (error instanceof TrattoError) {
switch (error.code) {
case 'VALIDATION_ERROR':
// Correggi il payload: riprovare invariato fallirà di nuovo.
break;
case 'TEMPLATE_NOT_PUBLISHED':
// Arriva dalle chiamate su campagne e flussi. Pubblica il template,
// poi rifai la chiamata: riprovare così com'è non serve.
break;
case 'RATE_LIMITED':
await backoff();
break;
case 'QUOTA_EXCEEDED':
// Non c'è nulla da riprovare.
break;
default:
throw error;
}
}
}Python
tratto-email solleva TrattoError con code, status_code, suggestion e
docs. Gli ultimi due richiedono la 1.2.0 o successiva e valgono None
quando la risposta non li contiene:
from tratto import TrattoError
try:
client.campaigns.unschedule('camp_xyz789')
except TrattoError as e:
print(e.code) # 'CONFLICT'
print(e.suggestion) # cosa fare invece, nelle parole dell'API
print(e.docs) # link a questa paginaPHP
tratto/email solleva TrattoException con errorCode, statusCode,
suggestion e docs. Gli ultimi due valgono null quando la risposta non
li contiene:
use Tratto\TrattoException;
try {
$tratto->campaigns->unschedule('camp_xyz789');
} catch (TrattoException $e) {
echo $e->errorCode, "\n"; // 'CONFLICT'
echo $e->suggestion, "\n"; // cosa fare invece, nelle parole dell'API
echo $e->docs, "\n"; // link a questa pagina
}È il caso per cui suggestion esiste: il 409 di campaigns.send e quello di
campaigns.unschedule sono un codice solo per più stati diversi, e a dire quale
hai incontrato c'è solo il testo. Vedi
Campagne.
Prossimo: Rate Limit · Idempotenza
Modifica questa pagina su GitHub
Ultimo aggiornamento