Allegati
Carica un file una volta, poi allegalo per id agli invii singoli e ai batch.
Richiede l'API 0.29.0 o successiva.
Inviare un file richiede due passi:
- Caricalo con
POST /v1/attachments. La risposta ti dà un id,att_seguito da 21 caratteri. - Usa l'id nel campo
attachmentsdiPOST /v1/emailso di qualsiasi email di un batch.
Un file caricato non è legato a una sola email. Finché non scade, lo stesso id si può allegare a quante email vuoi: un file che va a molte persone (condizioni di servizio, un listino) si carica una volta e ogni email di un batch lo richiama, invece di portarsi dietro la propria copia dei byte.
Piani che includono gli allegati
Gli allegati sono inclusi da Starter in su (Starter, Growth e Scale), non
nel Free. Su un workspace Free sia il caricamento sia un invio con
attachments rispondono 403 PLAN_UPGRADE_REQUIRED:
{
"error": {
"code": "PLAN_UPGRADE_REQUIRED",
"message": "This feature is not included in your plan. Upgrade to use it.",
"docs": "https://docs.tratto.email/en/docs/error-codes",
"suggestion": "Attachments are available from the Starter plan. The account owner can upgrade at https://app.tratto.email/settings/billing."
}
}Caricare un file
Il body di POST /v1/attachments è il file stesso, come byte grezzi: non
JSON e non multipart/form-data. Due header lo descrivono:
Content-Type: il tipo del file, per esempioapplication/pdf.Content-Disposition: il nome del file, comeattachment; filename="fattura.pdf". Per un nome con caratteri non ASCII usa la forma codificata,attachment; filename*=UTF-8''fattura%20n%C2%B01.pdf. Il nome va in questo header, non nella query string.
La chiave deve avere il permesso emails:send. Funzionano sia le chiavi live
sia quelle di test.
cURL
curl -X POST https://api.tratto.email/v1/attachments \
-H "Authorization: Bearer tratto_live_..." \
-H "Content-Type: application/pdf" \
-H 'Content-Disposition: attachment; filename="fattura.pdf"' \
--data-binary @fattura.pdfUsa --data-binary, non -d: -d toglie gli a capo e rovina il file.
Node.js
import { readFile } from 'node:fs/promises';
const response = await fetch('https://api.tratto.email/v1/attachments', {
method: 'POST',
headers: {
'Authorization': 'Bearer tratto_live_...',
'Content-Type': 'application/pdf',
'Content-Disposition': 'attachment; filename="fattura.pdf"',
},
body: await readFile('fattura.pdf'),
});
const { data } = await response.json();
console.log('Id allegato:', data.id);Python
import requests
with open('fattura.pdf', 'rb') as f:
response = requests.post(
'https://api.tratto.email/v1/attachments',
headers={
'Authorization': 'Bearer tratto_live_...',
'Content-Type': 'application/pdf',
'Content-Disposition': 'attachment; filename="fattura.pdf"',
},
data=f,
)
attachment = response.json()['data']
print(f"Id allegato: {attachment['id']}")Con un SDK, attachments.upload imposta i due header al posto tuo, anche
per i nomi di file con caratteri non ASCII, e restituisce l'oggetto data
della risposta. attachments.get e attachments.delete coprono gli altri due
endpoint.
SDK Node.js (richiede @tratto/email 1.7.0 o successiva)
import { readFile } from 'node:fs/promises';
const file = await tratto.attachments.upload(await readFile('fattura.pdf'), {
filename: 'fattura.pdf',
contentType: 'application/pdf',
});
console.log('Id allegato:', file.id);Il contenuto può essere un Buffer, un Uint8Array, un ArrayBuffer o un
Blob.
SDK Python (richiede tratto-email 2.2.0 o successiva)
with open('fattura.pdf', 'rb') as f:
file = client.attachments.upload(
f, filename='fattura.pdf', content_type='application/pdf'
)
print(f"Id allegato: {file['id']}")Il contenuto può essere bytes o un file aperto in modalità binaria.
SDK PHP (richiede tratto/email 1.2.0 o successiva)
$file = $tratto->attachments->upload(file_get_contents('fattura.pdf'), 'fattura.pdf', 'application/pdf');
echo 'Id allegato: ' . $file['id'];Il contenuto può essere una stringa di byte o uno stream leggibile, come
fopen('fattura.pdf', 'rb').
Risposta (201 Created):
{
"data": {
"id": "att_V1StGXR8Z5jdHi6BmyT0a",
"filename": "fattura.pdf",
"contentType": "application/pdf",
"size": 48213,
"sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"livemode": true,
"expiresAt": "2027-01-23T10:00:00.000Z"
}
}size è in byte e sha256 è il digest esadecimale dei byte che hai inviato.
livemode è true per un file caricato con una chiave live. Dopo expiresAt
il file non si può più allegare (vedi Conservazione).
Nome e tipo
- Il nome è obbligatorio: da 1 a 255 byte in UTF-8, senza caratteri di
controllo. Di un percorso resta solo l'ultimo segmento (
docs/fattura.pdfdiventafattura.pdf), e punti e spazi iniziali e finali vengono tolti. Un nome mancante o non valido risponde422 VALIDATION_ERRORcondetails[0].pathuguale a/filename. - Il tipo viene salvato come lo dichiari, in minuscolo e senza parametri
(
text/csv; charset=utf-8diventatext/csv). UnContent-Typemancante o malformato viene salvato comeapplication/octet-stream. Tratto non confronta il tipo dichiarato con il contenuto, salvo per i file vietati. - Caricare due volte lo stesso file dà due id, e ognuno conta nello spazio per gli allegati.
Frequenza dei caricamenti
Un caricamento al secondo per chiave API (per persona nella dashboard). Oltre,
l'API risponde 429 RATE_LIMITED con Retry-After: 1 e il messaggio
Rate limit exceeded: 1 attachment upload/second per API key. Quando ci sono
troppi caricamenti in corso nello stesso momento, risponde
503 SERVICE_UNAVAILABLE con Retry-After: 1 e
Too many uploads in progress. Retry after 1 second; nothing was stored.
In entrambi i casi si può riprovare senza rischi.
Dimensioni
- Ogni file: al massimo 25 MB (25.000.000 byte). Un body più grande
risponde
413 PAYLOAD_TOO_LARGEe non viene salvato niente. - Ogni email: i suoi allegati più il suo
htmletext, insieme, al massimo 25 MB (25.000.000 byte). Un file da 25 MB ci sta; due file da 15 MB no. Oltre il limite l'invio risponde422 VALIDATION_ERRORcondetails[0].pathuguale a/attachmentse un messaggio con entrambe le cifre, comeThis email is 30000000 bytes with its attachments, over the limit of 25000000 bytes.Il corpo di un template non viene contato quando invii contemplateId. - Numero di file: nessun limite, dentro i 25 MB dell'email.
Il server che riceve può accettare meno. 25 MB è quello che accetta Tratto, non quello che ogni casella riceve. Molti server di posta rifiutano i messaggi oltre una dimensione loro, spesso indicata fra 10 e 25 MB, e nel messaggio un allegato occupa circa un terzo in più del file, perché è codificato in base64. Un messaggio troppo grande per il server del destinatario torna indietro come bounce. Per i file grandi, invia un link.
File vietati
Il caricamento viene rifiutato con 422 VALIDATION_ERROR quando il file è uno
di questi. details[0].path dice cosa è stato rifiutato: /filename,
/contentType o /body.
- Estensioni che Amazon SES non invia: la
lista dei tipi di allegato non supportati
(
.exe,.bat,.js,.vbs,.msie le altre lì elencate), maiuscole o minuscole che siano. Conta l'ultima estensione del nome. - HTML e SVG: le estensioni
.html,.htm,.xhtml,.mht,.mhtmle.svg, e i tipitext/html,application/xhtml+xml,image/svg+xmlemultipart/related. - Eseguibili e script, riconosciuti dal contenuto qualunque sia il nome e
il tipo dichiarato: programmi Windows, Linux e macOS, script che iniziano con
#!e collegamenti di Windows. Rinominaresetup.exeinsetup.pdfnon lo fa passare.
Gli archivi come i file ZIP vengono accettati e non vengono aperti, quindi il loro contenuto non viene controllato.
Gli allegati non vengono analizzati contro il malware. I controlli qui sopra rifiutano tipi di file, niente di più: un PDF o un documento Office con contenuto dannoso viene accettato e inviato. Dei file che invii sei responsabile tu.
Allegare file a un'email
Passa gli id in attachments, un array di oggetti con un solo campo, id:
curl -X POST https://api.tratto.email/v1/emails \
-H "Authorization: Bearer tratto_live_..." \
-H "Content-Type: application/json" \
-d '{
"from": "billing@yourdomain.com",
"to": "user@example.com",
"subject": "La tua fattura",
"text": "In allegato trovi la fattura.",
"attachments": [{ "id": "att_V1StGXR8Z5jdHi6BmyT0a" }]
}'Il destinatario vede ogni file con il nome e il tipo con cui è stato caricato. L'invio viene controllato prima che venga scritto o addebitato qualcosa:
| Stato | Codice | Quando |
|---|---|---|
| 403 | PLAN_UPGRADE_REQUIRED | Il workspace è sul piano Free |
| 404 | NOT_FOUND | Un id che non esiste in questo workspace (Attachment 'att_...' not found.). Anche l'id di un altro workspace è un 404 |
| 422 | VALIDATION_ERROR | Un id ripetuto; un file caricato con una chiave live inviato con una chiave di test, o il contrario; un file che scade meno di un'ora dopo l'invio; l'email oltre i 25 MB con i suoi file |
GET /v1/emails/{id} restituisce i file dell'email com'erano quando è stata
accettata:
"attachments": [
{ "id": "att_V1StGXR8Z5jdHi6BmyT0a", "filename": "fattura.pdf", "contentType": "application/pdf", "size": 48213 }
]In un batch
Ogni email di un batch accetta anche attachments, con
le stesse regole. Carica una volta il file comune e usa lo stesso id in ogni
email. I byte dei file non contano nei limiti di 900 KB per email e 9 MiB per
batch, perché il batch contiene solo gli id. Un errore su un'email rifiuta
l'intero batch, e details[].path indica l'email.
Email programmate
Con scheduledAt, ogni file deve sopravvivere all'orario programmato di almeno
un'ora, altrimenti l'invio risponde 422. Riprogrammare con
PATCH /v1/emails/{id} ricontrolla i file: se uno scadrebbe prima del nuovo
orario, o è stato eliminato, l'API risponde 422 VALIDATION_ERROR e l'email
mantiene l'orario precedente.
Quando un file manca all'invio
Il file viene letto quando l'email parte, non quando viene accettata. Se a quel punto è stato eliminato o è scaduto, l'email non parte:
- il suo status diventa
failed, confailureReasonuguale aattachment_unavailablesuGET /v1/emails/{id}; - un evento
failedcondata.reasonuguale aattachment_unavailablearriva ai webhook iscritti afailed(vedi Evento failed); - la quota dell'email viene restituita, come se non fosse mai stata inviata.
Conservazione
Un file caricato con una chiave live scade 90 giorni dopo il caricamento;
uno caricato con una chiave di test, dopo 7 giorni. Lo dice expiresAt
nella risposta del caricamento. Dopo quella data il file non si può più
allegare, e i suoi byte vengono rimossi nel giro di qualche giorno.
Per continuare a inviare un file più a lungo, caricalo di nuovo e usa il nuovo id.
Spazio per gli allegati
Ogni workspace può tenere 1 GB (1.000.000.000 byte) di allegati alla volta,
live e test insieme, su ogni piano che include gli allegati. Ogni caricamento
conta finché non viene eliminato o non scade; i file scaduti vengono liberati
da una pulizia che gira una volta al giorno. Un caricamento che supererebbe il
limite risponde 429 QUOTA_EXCEEDED, e il messaggio dice quanto spazio è in
uso:
{
"error": {
"code": "QUOTA_EXCEEDED",
"message": "Attachment storage limit reached: 999000000 of 1000000000 bytes used, this file is 2000000 bytes.",
"docs": "https://docs.tratto.email/en/docs/error-codes",
"suggestion": "Delete attachments you no longer need (DELETE /v1/attachments/{id}); expired ones are released automatically."
}
}Riprovare non serve: elimina i file che non ti servono più, o aspetta che alcuni scadano.
Leggere ed eliminare un allegato
GET /v1/attachments/{id} restituisce gli stessi campi della risposta del
caricamento (permesso emails:read). Restituisce solo i metadati: i byte di un
file non vengono mai restituiti. Non esiste un endpoint che elenchi gli
allegati, quindi conserva gli id che ricevi.
DELETE /v1/attachments/{id} rimuove il file e libera subito il suo spazio
(permesso emails:send). Risponde 204 senza body. Le email già accettate con
quel file e non ancora partite falliranno con attachment_unavailable, come
descritto sopra.
curl -X DELETE https://api.tratto.email/v1/attachments/att_V1StGXR8Z5jdHi6BmyT0a \
-H "Authorization: Bearer tratto_live_..."Test mode
Un file caricato con una chiave di test è un file di test (livemode: false):
si può allegare solo alle email inviate con una chiave di test, e un file live
solo alle email inviate con una chiave live. In test mode
i file vengono controllati quando l'email viene accettata, e il simulatore non
li legge mai.
Errori
| Rotta | Stato | Codice | Quando |
|---|---|---|---|
POST /v1/attachments | 403 | PLAN_UPGRADE_REQUIRED | Il workspace è sul piano Free |
POST /v1/attachments | 403 | FORBIDDEN | La chiave non ha il permesso emails:send |
POST /v1/attachments | 413 | PAYLOAD_TOO_LARGE | Il file supera i 25 MB |
POST /v1/attachments | 422 | VALIDATION_ERROR | Body vuoto, nome mancante o non valido, un file vietato |
POST /v1/attachments | 429 | QUOTA_EXCEEDED | Lo spazio del workspace è pieno |
POST /v1/attachments | 429 | RATE_LIMITED | Più di un caricamento al secondo |
POST /v1/attachments | 503 | SERVICE_UNAVAILABLE | Troppi caricamenti in corso; riprova dopo Retry-After |
GET, DELETE /v1/attachments/{id} | 404 | NOT_FOUND | Nessun allegato con quell'id in questo workspace |
POST /v1/emails, POST /v1/emails/batch | 403, 404, 422 | Vedi Allegare file a un'email |
Modifica questa pagina su GitHub
Ultimo aggiornamento