Allegati
Allega file a email transazionali, campagne, automazioni e inbox agente.
| GET/POST | /api/v1/attachments | Elenca o carica un allegato riusabile: `content` base64 per l'upload immediato, `size` per ottenere una `uploadUrl` firmata (file grandi).(scope: email:send|inbox:send) |
| GET/DELETE | /api/v1/attachments/:id | Metadati + URL di download firmata, oppure eliminazione.(scope: email:send|inbox:send) |
| POST | /api/v1/attachments/:id/confirm | Conferma un upload firmato (allinea dimensione e Content-Type reali).(scope: email:send|inbox:send) |
Il campo attachments è accettato da tutti i percorsi di invio email:
| Percorso | Endpoint |
|---|---|
| Transazionale | POST /api/v1/emails, POST /api/v1/messages (canale email) |
| Marketing (broadcast) | POST /api/v1/campaigns |
| Marketing (automazioni) | nodo message del Journey, campo attachmentIds |
| Agent Email | POST /api/v1/inboxes/:id/send, POST /api/v1/inboxes/:id/threads/:threadId/reply |
Le tre sorgenti
Ogni elemento di attachments indica esattamente una sorgente:
| Forma | Quando usarla |
|---|---|
{ "id": "<uuid>" } | Allegato già caricato con POST /api/v1/attachments. È la via consigliata: carichi il file una volta e lo riusi su quanti invii vuoi. |
{ "filename": "...", "content": "<base64>" } | File piccoli: il contenuto viaggia nel body della richiesta, quindi il limite pratico sono i pochi MB accettati dal body HTTP. |
{ "filename": "...", "url": "https://..." } | File già pubblicati su una URL https pubblica: lo scarichiamo noi (host privati e loopback sono bloccati). |
Campi opzionali comuni: contentType (assente, viene dedotto dal nome file) e contentId.
Con contentId l'allegato diventa inline e si referenzia nell'HTML come cid:<valore>:
{
"html": "<p>Ecco il logo: <img src=\"cid:logo\" /></p>",
"attachments": [{ "filename": "logo.png", "content": "<base64>", "contentId": "logo" }]
}Limiti
| Limite | Valore |
|---|---|
| Allegati per email | 20 |
| Dimensione singola | 10 MB |
| Dimensione complessiva | 25 MB |
Le estensioni eseguibili (exe, bat, js, jar, msi, vbs, …) sono rifiutate con 422:
i filtri dei provider le trattano come malware e il messaggio finirebbe in spam o in bounce,
danneggiando la reputazione del dominio. Per quei file usa un link.
Allegato inline in un invio
import { readFileSync } from "node:fs";
await sending.emails.send({
from: "Acme <[email protected]>",
to: "[email protected]",
subject: "La tua fattura",
html: "<p>In allegato la fattura di luglio.</p>",
attachments: [
{ filename: "fattura-2026-07.pdf", content: readFileSync("fattura.pdf").toString("base64") },
],
idempotencyKey: "fattura-sara-2026-07",
});POST /api/v1/attachments · allegato riusabile
Due modalità, a seconda della dimensione del file.
Upload immediato (base64 nel body, file piccoli):
curl -X POST "https://sending.dev/api/v1/attachments" \
-H "Authorization: Bearer <LA_TUA_API_KEY>" \
-H "Content-Type: application/json" \
-d '{ "filename": "listino.pdf", "content": "<base64>" }'
# → 201 { "attachment": { "id": "...", "status": "ready", ... } }Upload firmato (file grandi): chiedi una uploadUrl, fai il PUT del file, conferma.
Crea l'allegato dichiarando dimensione e tipo
curl -X POST "https://sending.dev/api/v1/attachments" \
-H "Authorization: Bearer <LA_TUA_API_KEY>" \
-H "Content-Type: application/json" \
-d '{ "filename": "catalogo.pdf", "contentType": "application/pdf", "size": 4194304 }'
# → 201 { "attachment": { "id": "...", "status": "pending" }, "uploadUrl": "https://...", "expiresIn": 900 }Carica il file sulla uploadUrl
curl -X PUT "<UPLOAD_URL>" \
-H "Content-Type: application/pdf" \
--data-binary @catalogo.pdfLa URL firmata vincola Content-Type e Content-Length a quanto dichiarato al passo 1.
Conferma (facoltativo)
curl -X POST "https://sending.dev/api/v1/attachments/<ID>/confirm" \
-H "Authorization: Bearer <LA_TUA_API_KEY>"La conferma allinea dimensione e Content-Type reali. Se la salti non perdi niente: la stessa verifica avviene automaticamente al primo uso dell'allegato in un invio.
L'SDK TypeScript incapsula i tre passi:
const attachment = await sending.attachments.upload({
filename: "catalogo.pdf",
data: readFileSync("catalogo.pdf"),
contentType: "application/pdf",
});
await sending.emails.send({
from: "Acme <[email protected]>",
to: "[email protected]",
subject: "Il catalogo 2026",
html: "<p>Eccolo.</p>",
attachments: [{ id: attachment.id }],
idempotencyKey: "catalogo-sara-001",
});Campagne e automazioni
Su una campagna gli allegati si dichiarano alla creazione e valgono per tutti i destinatari (il file sta su storage una volta sola, non viene duplicato per contatto):
curl -X POST "https://sending.dev/api/v1/campaigns" \
-H "Authorization: Bearer <LA_TUA_API_KEY>" \
-H "Content-Type: application/json" \
-d '{ "name": "Listino 2026", "fromAddress": "[email protected]", "listId": "<UUID>",
"subject": "Il nuovo listino", "html": "<p>In allegato.</p>",
"attachments": [{ "id": "<ATTACHMENT_ID>" }] }'Nelle automazioni il nodo message porta attachmentIds (solo id di allegati già caricati):
il DAG resta leggero e sostituire il file non richiede una nuova versione del journey.
{
"id": "welcome-mail",
"type": "message",
"preferredChannel": "email",
"fallbackChain": ["email"],
"templateByChannel": { "email": "<TEMPLATE_ID>" },
"attachmentIds": ["<ATTACHMENT_ID>"],
"next": null
}Gestione
| Operazione | Endpoint |
|---|---|
| Elenco | GET /api/v1/attachments |
| Metadati + download firmato (15 min) | GET /api/v1/attachments/:id |
| Eliminazione | DELETE /api/v1/attachments/:id |
Lo scope richiesto è email:send oppure inbox:send. GET /api/v1/emails/:id riporta gli
allegati dell'invio (id, filename, contentType, size).