Allegati

Allega file a email transazionali, campagne, automazioni e inbox agente.

GET/POST/api/v1/attachmentsElenca 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/:idMetadati + URL di download firmata, oppure eliminazione.(scope: email:send|inbox:send)
POST/api/v1/attachments/:id/confirmConferma 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:

PercorsoEndpoint
TransazionalePOST /api/v1/emails, POST /api/v1/messages (canale email)
Marketing (broadcast)POST /api/v1/campaigns
Marketing (automazioni)nodo message del Journey, campo attachmentIds
Agent EmailPOST /api/v1/inboxes/:id/send, POST /api/v1/inboxes/:id/threads/:threadId/reply

Le tre sorgenti

Ogni elemento di attachments indica esattamente una sorgente:

FormaQuando 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

LimiteValore
Allegati per email20
Dimensione singola10 MB
Dimensione complessiva25 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.pdf

La 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

OperazioneEndpoint
ElencoGET /api/v1/attachments
Metadati + download firmato (15 min)GET /api/v1/attachments/:id
EliminazioneDELETE /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).

Eliminare un allegato non blocca gli invii già accodati che lo referenziano: partiranno senza quel file.
Schema completo e "try it" nella API reference interattiva (OpenAPI).