Invio email transazionale.
| POST | /api/v1/emails | Invia un'email transazionale. `idempotencyKey` nel body obbligatoria (min 8 char); `from`/`replyTo` accettano 'Nome <[email protected]>'.(scope: email:send) |
| GET | /api/v1/emails/:id | Stato di un invio (metadati + timeline eventi). |
| POST | /api/v1/messages | Invio transazionale unificato email/WhatsApp/Telegram.(scope: messages:send) |
POST /api/v1/emails
Parametri del body (JSON):
| Campo | Tipo | Obblig. | Note |
|---|---|---|---|
to | string o array | sì | Uno o più destinatari: [email protected], Nome <[email protected]>, più indirizzi separati da virgola nella stessa stringa, oppure un array. |
from | string | sì | [email protected] oppure Nome <[email protected]>. Il dominio deve essere verificato. |
subject | string | sì* | Min 1 char. Opzionale se il template fornisce il subject. |
html | string | sì* | HTML del messaggio. In alternativa usa templateId. |
text | string | no | Versione testo semplice (parte multipart). |
replyTo | string | no | Reply-To: [email protected] o Nome <[email protected]>. |
cc | string o array | no | Copie visibili: compaiono nell'header Cc. Stesse forme di to. |
bcc | string o array | no | Copie nascoste: non compaiono in nessun header del messaggio. Stesse forme di to. |
templateId | string | no | Id (uuid) o nome del template del tenant; alternativo a html. |
variables | object | no | Variabili di interpolazione del template. |
attachments | array | no | Allegati: { id }, { filename, content } (base64) o { filename, url }. Vedi Allegati. |
idempotencyKey | string | sì | Min 8 caratteri. Previene i doppi invii sui retry. Va nel body, non in un header. |
disableTracking | boolean | no | Disattiva pixel/redirect per questo invio (default false). |
disableUtm | boolean | no | Salta l'auto-tagging UTM dei link (default false). |
subject/html sono richiesti se non usi un templateId che li fornisce.Massimo 50 destinatari per messaggio (to + cc + bcc), limite di SES: oltre, la
risposta è 422 too_many_recipients. Ogni destinatario consuma una unità della quota
mensile del piano. Le copie in suppression (bounce, complaint, unsubscribe) vengono tolte
dall'invio e riportate nella risposta in droppedRecipients; la richiesta è un 409 solo
quando non resta nemmeno un destinatario to a cui inviare. Aperture e click sono per
messaggio, non per destinatario: non distinguono chi delle copie ha aperto.
Ogni campo indirizzo accetta sia [email protected] sia Nome <[email protected]>, e to, cc e bcc
prendono anche più indirizzi insieme, come array o separati da virgola dentro una sola
stringa ("[email protected], [email protected]"). Una virgola dentro un display name fra virgolette
non separa niente: "Neri, Sara" <[email protected]> è un destinatario solo. Più to finiscono
nell'header To, non in Cc, e GET /api/v1/emails/{id} li restituisce in toAddresses.
Risposte: 202 ({ id, status: "queued" }), 200 ({ status: "duplicate", idempotencyKey, id }),
422 (validazione), 402 (quota piano), 403 (domain_not_verified), 409 (suppressed).
Esempio
import { Sending } from "sending-sdk";
const sending = new Sending({ apiKey: process.env.SENDING_API_KEY! });
const { id } = await sending.emails.send({
from: "Acme <[email protected]>",
to: "[email protected]",
subject: "Ciao",
html: "<p>Hey</p>",
replyTo: "Supporto <[email protected]>",
idempotencyKey: "ciao-sara-001",
});POST /api/v1/emails/batch
Fino a 100 email in una richiesta. Il body è { "emails": [...] } e ogni elemento è
esattamente il body di POST /api/v1/emails, idempotencyKey compresa.
L'esito è parziale, non tutto o niente: la risposta è 207 e results è allineato per
indice all'array inviato, quindi un indirizzo invalido su 60 non impedisce gli altri 59.
| Campo del risultato | Note |
|---|---|
index | Posizione nell'array emails inviato. |
status | accepted oppure failed. |
id | Id del messaggio, presente sugli accettati (duplicati compresi). |
duplicate | true se quella idempotencyKey era già stata accettata: non è un errore. |
droppedRecipients | Copie escluse perché in suppression. |
error | { code, message } sui falliti: validation_error, domain_not_verified, suppressed, template_not_found, quota_exceeded, too_many_recipients. |
L'idempotenza vale per elemento: un batch ritentato non produce doppi invii, e gli
elementi già accettati tornano accepted con duplicate: true. Ai fini dei
limiti di frequenza la chiamata conta una richiesta.
const esito = await sending.emails.sendBatch([
{ from: "Acme <[email protected]>", to: "[email protected]", subject: "Ciao", html: "<p>hey</p>", idempotencyKey: "b-001" },
{ from: "Acme <[email protected]>", to: "[email protected]", subject: "Ciao", html: "<p>hey</p>", idempotencyKey: "b-002" },
]);
for (const r of esito.results) {
if (r.status === "failed") console.error(`elemento ${r.index}: ${r.error?.code}`);
}Risposta:
{
"accepted": 2,
"failed": 1,
"results": [
{ "index": 0, "status": "accepted", "id": "8f1c…" },
{ "index": 1, "status": "accepted", "id": "0b22…", "duplicate": true },
{ "index": 2, "status": "failed", "error": { "code": "suppressed", "message": "complaint" } }
]
}GET /api/v1/emails/:id
Stato di un invio: usa l'id restituito dall'invio.
const status = await sending.emails.get(id);
console.log(status.status, status.events);Ritorna status, metadati (to, from, subject, provider, createdAt), gli attachments
dell'invio (id, filename, contentType, size) e la timeline
events (sent, delivered, open, click, bounce, complaint).