Invio di email
POST /v1/email/messages invia una singola email. Fornisci un mittente, i destinatari e il contenuto in un payload JSON. L'API restituisce 202 Accepted con un ID messaggio, poi consegna l'email in modo asincrono. Consulta il reference di API per gli schemi completi.
Un invio minimale
Il payload valido più piccolo è un from, almeno un destinatario to, un subject e un corpo (html, text o entrambi). L'indirizzo from deve trovarsi su un dominio che hai verificato in questo spazio di lavoro oppure sul dominio di onboarding.
const msg = await bird.email.send({
from: { email: "onboarding@messagebird.dev", name: "Bird" },
to: ["delivered@messagebird.dev"],
subject: "Hello from Bird",
html: "<p>My first Bird email.</p>",
});
console.log(msg.id, msg.status); // "em_…", "accepted"msg = client.email.send(
from_={"email": "onboarding@messagebird.dev", "name": "Bird"},
to=["delivered@messagebird.dev"],
subject="Hello from Bird",
html="<p>My first Bird email.</p>",
)
print(msg.id, msg.status)package main
import (
"context"
"fmt"
"log"
"os"
bird "github.com/messagebird/bird-sdk-go"
"github.com/messagebird/bird-sdk-go/option"
)
func main() {
client, err := bird.NewClient(option.WithAPIKey(os.Getenv("BIRD_API_KEY")))
if err != nil {
log.Fatal(err)
}
msg, err := client.Email.Send(context.Background(), bird.EmailSendParams{
From: "onboarding@messagebird.dev",
To: []string{"delivered@messagebird.dev"},
Subject: "Hello from Bird",
HTML: "<p>My first Bird email.</p>",
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Status)
}$message = $bird->email->send(
from: 'Bird <onboarding@messagebird.dev>',
to: ['delivered@messagebird.dev'],
subject: 'Hello from Bird',
html: '<p>My first Bird email.</p>',
);
echo $message->getId(), ' ', $message->getStatus();bird email send \
--from hello@yourdomain.com \
--html '<p>It works.</p>' \
--subject 'Hello from Bird' \
--to delivered@messagebird.devcurl -X POST https://us1.platform.bird.com/v1/email/messages \
-H "Authorization: Bearer bk_us1_..." \
-H "Content-Type: application/json" \
-d '{
"from": "hello@yourdomain.com",
"to": ["delivered@messagebird.dev"],
"subject": "Hello from Bird",
"html": "<p>It works.</p>"
}'Usa il tuo host regionale (https://us1.platform.bird.com o https://eu1.platform.bird.com) con una chiave bk_{region}_... corrispondente.
L'esempio di invio usa delivered@messagebird.dev, un indirizzo sandbox che accetta sempre la posta. L'API rifiuta i domini segnaposto con un 422: example.com, example.net, example.org, example.edu, test.com e qualsiasi dominio sotto i TLD riservati .test, .example, .invalid o .localhost. Un invio a questi domini può solo rimbalzare, il che danneggia la tua reputazione di mittente.
Invio prima della verifica di un dominio
Durante l'onboarding puoi inviare dal nostro dominio di onboarding condiviso, onboarding@messagebird.dev. Questi invii saltano il controllo del dominio, ma raggiungono solo i membri verificati del tuo spazio di lavoro e gli indirizzi sandbox, con un limite giornaliero di destinatari. Il quickstart contiene le regole e i limiti esatti.
Costruire il payload
Destinatari
to, cc e bcc accettano ciascuno fino a 50 indirizzi, e to ne richiede almeno uno. Ogni voce è una stringa email semplice, una stringa mailbox RFC 5322 (Jane <jane@acme.com>) o un oggetto con un nome visualizzato opzionale.
I destinatari presenti nella lista di soppressione dello spazio di lavoro non fanno fallire la richiesta. Viene comunque restituito un 202, e ogni destinatario soppresso appare sugli endpoint di lettura come status: rejected con la motivazione recipient_suppressed, anche quando si tratta di tutti i destinatari dell'invio.
Contenuto
subject è obbligatorio per gli invii inline, fino a 998 caratteri. Fornisci html, text o entrambi, ciascuno fino a 524.288 caratteri. Invia entrambi quando possibile: un client che non riesce a visualizzare l'HTML ripiega sulla parte di testo.
Per personalizzare il contenuto inline, inserisci token {{ variable }} nell'oggetto o nel corpo e passa i relativi valori in parameters, fino a 16 KB serializzati. Un unico set di valori vale per tutti i destinatari dell'invio, e un token senza chiave corrispondente viene visualizzato vuoto. Per i contenuti che riutilizzi, invia invece un template.
Includi parameters, anche come oggetto vuoto ({}), per elaborare l'oggetto e il corpo come Liquid. Omettilo per inviare token come {{ animal }} esattamente come scritti. Ogni nome di parametro è una singola parola, ad esempio first_name; i nomi con punti e il nome riservato bird vengono rifiutati. Una sintassi Liquid non valida e tag o filtri non supportati restituiscono 422.
I valori inseriti nell'HTML vengono sottoposti a escape, così non possono alterare il markup circostante. Per un link completo o l'URL di un'immagine, usa {{ link }} senza url_encode. Per un valore all'interno della query di un URL, codifica quel valore esplicitamente, ad esempio https://example.com/search?q={{ query | url_encode }}.
Reply-to e header personalizzati
reply_to accetta da 1 a 25 indirizzi, negli stessi formati dei destinatari. Ogni risposta di un destinatario va a tutti questi indirizzi, quindi di solito se ne indicano uno o due.
headers è un oggetto stringa-stringa per i tuoi header, ad esempio {"X-Campaign": "spring-2026"}, con un massimo di 25 header e valori fino a 998 caratteri. Due tipi di header vengono rifiutati con un 422:
- Header di indirizzamento e di piattaforma. Imposta l'indirizzamento del messaggio tramite i campi dedicati (
from,to,cc,bcc,reply_to,subject). Quei nomi, e gli header che generiamo per te (Content-Type,Content-Transfer-Encoding,DKIM-Signature,Received,Return-Path), non possono essere impostati qui. - Qualsiasi valore con un ritorno a capo o un avanzamento riga.
Gli invii transazionali accettano List-Unsubscribe e List-Unsubscribe-Post. Gli invii marketing usano per impostazione predefinita la disiscrizione gestita da Bird.
Tracciamento
track_opens e track_clicks hanno entrambi il valore predefinito true. Imposta uno dei due a false per saltare l'iniezione del pixel di apertura o la riscrittura dei link su questo invio. Questi flag lasciano intatti i pixel di tracciamento e i link già presenti nel contenuto che invii. Tracciamento e metriche descrive cosa ciascuno modifica nel messaggio.
Categoria e pool di IP
category classifica il contenuto e imposta la policy di soppressione: marketing blocca la consegna per qualsiasi motivo di soppressione e per qualsiasi opt-out, mentre transactional consegna nonostante una soppressione per reclamo o un opt-out solo marketing (un opt-out registrato per tutti i messaggi lo blocca comunque). Il valore predefinito è la categoria del template su un invio con template e marketing altrimenti, quindi imposta transactional esplicitamente per ricevute, reset di password e altra posta operativa. Categorie tratta la scelta. La posta inviata tramite SMTP prende invece la categoria dalla configurazione SMTP della chiave.
ip_pool_id seleziona il pool di invio: un ID pool (ipp_...), oppure ipp_shared per instradare esplicitamente attraverso il pool condiviso. Omettilo per usare il pool predefinito della tua organizzazione. Un pool sconosciuto, o uno senza IP dedicati disponibili per l'invio, viene rifiutato con un 422.
Riferimento dei campi
| Campo | Tipo | Obbligatorio | Limiti e note |
|---|---|---|---|
from | address | sì | Deve trovarsi su un dominio verificato o sul dominio di onboarding |
to | address[] | sì | Da 1 a 50 |
cc, bcc | address[] | no | Fino a 50 ciascuno |
subject | string | invii inline | Fino a 998 caratteri; omettere negli invii con template |
html, text | string | almeno uno | Fino a 524.288 caratteri ciascuno; omettere negli invii con template |
reply_to | address[] | no | Da 1 a 25; le risposte raggiungono tutti gli indirizzi elencati |
headers | object (string → string) | no | Fino a 25; nomi riservati rifiutati (vedi header personalizzati) |
parameters | object | no | Valori per {{ tokens }} nel contenuto inline; fino a 16 KB serializzati; condivisi tra i destinatari |
tags | {name, value}[] | no | Fino a 20; nome ≤ 32 caratteri, valore ≤ 64 caratteri; solo [A-Za-z0-9_-]; nomi univoci per invio |
metadata | object | no | JSON arbitrari, fino a 2 KB serializzati |
reference | string | no | Da 1 a 255 caratteri; corrispondenza esatta per ?reference= nell'elenco messaggi |
track_opens | boolean | no | Predefinito true |
track_clicks | boolean | no | Predefinito true |
category | string | no | marketing o transactional; predefinito quello del template su un invio con template, altrimenti marketing |
ip_pool_id | string | no | ipp_... o ipp_shared; omettere per il pool predefinito della tua organizzazione |
template | object | no | Invia un template pubblicato per id o slug, con parameters per le sue variabili e un language opzionale |
attachments | object[] | no | Fino a 20; vedi allegati |
scheduled_at | RFC 3339 timestamp | no | Programma l'invio di contenuto inline o di un template; vedi invio programmato |
Invio con un template
I template possono usare i valori bird.contact.* del contatto che corrisponde a ciascun indirizzo To. I destinatari Cc e Bcc ricevono una copia dell'email personalizzata di ciascun destinatario To. Questo funziona anche nel API in batch e per gli invii programmati; consulta Personalizzazione con variabili per un esempio con più destinatari.
Anziché contenuto inline, invia un template pubblicato: imposta template su un oggetto che lo identifica per id (emt_...) o per slug, esattamente uno dei due, con i valori delle variabili in template.parameters. Ometti subject, html e text, perché il template li contiene già.
const msg = await bird.email.send({
from: { email: "onboarding@messagebird.dev", name: "Bird" },
to: ["delivered@messagebird.dev"],
category: "transactional",
template: {
slug: "welcome-email",
parameters: { first_name: "Jane" },
},
});
console.log(msg.id, msg.status);msg = client.email.send(
from_={"email": "onboarding@messagebird.dev", "name": "Bird"},
to=["delivered@messagebird.dev"],
category="transactional",
template="welcome-email",
parameters={"first_name": "Jane"},
)
print(msg.id, msg.status)msg, err := client.Email.Send(context.Background(), bird.EmailSendParams{
From: "onboarding@messagebird.dev",
To: []string{"delivered@messagebird.dev"},
Category: "transactional",
Template: "welcome-email",
Parameters: map[string]any{"first_name": "Jane"},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Status)$message = $bird->email->send(
from: 'Bird <onboarding@messagebird.dev>',
to: ['delivered@messagebird.dev'],
category: 'transactional',
template: (new EmailMessageSendRequestTemplate())
->setSlug('welcome-email')
->setParameters(['first_name' => 'Jane']),
);
echo $message->getId(), ' ', $message->getStatus();bird email send \
--category transactional \
--from hello@yourdomain.com \
--parameters '{"first_name":"Jane"}' \
--template welcome-email \
--to delivered@messagebird.devcurl -X POST https://us1.platform.bird.com/v1/email/messages \
-H "Authorization: Bearer bk_us1_..." \
-H "Content-Type: application/json" \
-d '{
"from": "hello@yourdomain.com",
"to": ["delivered@messagebird.dev"],
"category": "transactional",
"template": {
"slug": "welcome-email",
"parameters": { "first_name": "Jane" }
}
}'Il contenuto di un template è Liquid, quindi oltre alla semplice sostituzione {{ variable }} può usare filtri, condizionali {% if %} e cicli {% for %}. Personalizzazione con variabili elenca i pochi costrutti rifiutati alla pubblicazione. In template.parameters inserisci i valori per i parametri del template, indicizzati per nome. Se ne ometti uno, l'invio viene rifiutato con un 422 che lo indica. L'invio supporta gli stessi tags, metadata, tracciamento e allegati dell'email inline. Cosa è specifico di un invio con template:
- Inline o con template, mai entrambi. Inviare
templateinsieme asubject,htmlotextviene rifiutato con un422. L'API rifiuta anche i valori delle variabili nel campo di primo livelloparameters; in un invio con template vanno intemplate.parameters. birdè l'unico nome riservato. Un percorso placeholder che inizia conbird.identifica dati nostri, come il link di disiscrizione o il record del contatto del destinatario, quindi una chiavetemplate.parametersnon può chiamarsibird. Tutte le altre chiavi sono definibili da te, e ognuna è una singola parola:{"order_number": "A-1043"}riempie{{ order_number }}.- Un template può essere inviato subito o in un secondo momento. Aggiungi
scheduled_atper programmare l'invio. Al momento dell'accettazione salviamo la versione pubblicata, la lingua selezionata, i parametri del chiamante e i valori dei contatti, quindi le modifiche successive ai contatti o ai fallback non cambiano i messaggi già accettati. Se elimini il template prima dell'orario di invio, il messaggio viene rifiutato congeneration_failure. - Personalizza con gli attributi del contatto. Consulta requisiti dei contatti e saluti predefiniti.
- La corrispondenza dei contatti ignora i nomi visualizzati. Gli indirizzi email vengono convertiti in minuscolo, mantenendo punti e tag con il segno più. Se più contatti condividono quell'indirizzo, l'invio restituisce
EmailTemplateContactAmbiguous; aggiorna i record duplicati prima di riprovare. - L'invio usa i permessi email. Una chiave con
emails:writepuò inviare un template pubblicato senza il permesso di individuazione dei contatti.emails:readconcede l'accesso al contenuto non sensibile del messaggio risultante. L'individuazione di contatti e proprietà mantiene i permessi esistenti. - Un invio usa la versione pubblicata del template. Le bozze non vengono mai inviate. Un template sconosciuto viene rifiutato con un
404, e un template senza versione pubblicata con un422. languageseleziona una delle lingue del template. Omettilo per inviare la lingua predefinita del template. Chiedi una lingua che il template non ha, e la sua impostazioneon_missing_languagedecide se viene inviata la corrispondenza più vicina o se l'invio viene rifiutato. Un template che impostalanguage_source_requiredrifiuta un invio che non specifica alcuna lingua.- La categoria del template è un valore predefinito, e la tua lo sovrascrive. Ometti
categorye l'invio eredita quella del template, quindi un template transazionale non ha bisogno di ripeterla a ogni chiamata.
Template email tratta la creazione, la pubblicazione e i costrutti che un template può contenere.
Leggere il contenuto per un destinatario
La cronologia dei messaggi con template usa i valori dei contatti salvati al momento dell'accettazione dell'invio, anche se in seguito modifichi o elimini il contatto. Ogni consegna in copia ha il proprio ID destinatario.
Elenca i destinatari del messaggio per ottenerne gli ID, poi passa recipient_id a Get stored message content. Un nuovo messaggio con template con più snapshot di destinatari restituisce 422 senza quel selettore. Un singolo destinatario viene selezionato automaticamente; un destinatario che appartiene a un altro messaggio restituisce 404.
Le risposte GET e di elenco dei messaggi usano l'oggetto accettato per il primo destinatario To, identificato da subject_recipient_id. L'elenco dei destinatari fornisce l'oggetto di ciascun destinatario. Questi oggetti restano disponibili anche quando l'archiviazione del corpo è disabilitata. Le letture del contenuto seguono comunque le impostazioni di archiviazione e conservazione del messaggio, inclusi 404 quando il contenuto non è archiviato, 425 mentre è in attesa e 410 dopo la scadenza.
Se uno snapshot di destinatario conservato diventa non disponibile, lo stato del messaggio e l'oggetto rappresentativo restano leggibili, ma gli oggetti per destinatario vengono omessi e il contenuto non è disponibile. Le letture non consultano i valori attuali dei contatti per ricostruire un invio precedente.
Tag e metadati a confronto
Questi campi associano dati tuoi a un invio e differiscono nel modo in cui li interroghi successivamente:
- I
tagssono coppie{name, value}strutturate: fino a 20 per invio, nome fino a 32 caratteri, valore fino a 64, solo lettere ASCII, cifre, underscore e trattino, e nomi univoci all'interno dell'invio. I tag sono dimensioni di filtro, quindi puoi filtrare l'elenco messaggi per tag e segmentare analytics e aggregazioni dashboard per tag. Usali per etichette a bassa cardinalità comecampaign,experiment_variantosource. - I
metadatasono un oggetto JSON arbitrario, fino a 2 KB serializzati. Li conserviamo, li restituiamo nelle letture API e li includiamo in ogni evento webhook, quindi sono adatti a contesti che vuoi ricevere indietro: ID interni, chiavi esterne, payload strutturati. referenceè una stringa da 1 a 255 caratteri con cui vuoi ritrovare il messaggio, ad esempio un numero d'ordine o di ticket.GET /v1/email/messages?reference=la confronta in modo esatto, distinguendo maiuscole e minuscole, e più messaggi possono condividere lo stesso valore. Viene restituita nelle letture e inclusa negli eventi webhook del messaggio. Impostala solo se devi cercare in base a essa, e inserisci tutti gli altri dati inmetadata.
Ogni evento webhook include i tag e i metadati insieme agli ID di correlazione (email_id, recipient_id), così puoi riconciliare i dati con i tuoi record senza una seconda ricerca. I nomi dei tag e le chiavi di metadati di primo livello che iniziano con __bird vengono rifiutati. Non è necessario codificare dispositivo, area geografica, provider di casella di posta, tipo di bounce o dominio del destinatario in nessuno dei due campi, perché registriamo già ciascuno di questi come dimensione di analytics.
{
"tags": [{ "name": "campaign", "value": "onboarding" }],
"metadata": { "user_id": "usr_12345", "order_id": "ord_98765" },
"reference": "ord_98765"
}Allegati
attachments accetta fino a 20 file per messaggio, come byte codificati in base64 inline. Rifiutiamo un invio la cui dimensione stimata del messaggio generato supera 20 MB, misurata dopo la codifica base64, quindi mantieni il contenuto grezzo degli allegati a 15 MB o meno per avere margine. Allegati contiene il contratto dei campi, le immagini inline, i tipi di file bloccati e come scaricare un allegato.
Cosa significa un 202
Un invio riuscito restituisce 202 Accepted con un ID messaggio prefissato da em_ e status: accepted:
{
"id": "em_01ky7ma8y2es1s2akzk53tmjn0",
"status": "accepted",
"category": "marketing",
"from": { "email": "hello@yourdomain.com" },
"to": [{ "email": "delivered@messagebird.dev" }],
"subject": "Hello from Bird",
"accepted_count": 1,
"processed_count": 0,
"delivered_count": 0,
"deferred_count": 0,
"bounced_count": 0,
"complained_count": 0,
"rejected_count": 0,
"open_count": 0,
"click_count": 0,
"track_opens": true,
"track_clicks": true,
"created_at": "2026-07-23T13:58:20.866Z"
}Il 202 significa che l'invio è stato accettato in modo durevole. Gli errori che puoi correggere tornano sulla richiesta stessa come 422: un dominio mittente non verificato o un campo non valido. Gli esiti per destinatario (consegnato, respinto, differito, segnalato) arrivano successivamente tramite i webhook e gli endpoint di lettura del messaggio.
Da questo derivano due conseguenze:
- Le letture restituiscono lo stato senza il corpo.
GET /v1/email/messages/{message_id}restituisce lo stato del messaggio e dei destinatari, mai il corpohtmlotext. Quando l'archiviazione dei contenuti è abilitata per lo spazio di lavoro, i corpi archiviati restano disponibili fino a 30 giorni daGET /v1/email/messages/{message_id}/content. - Una lettura può seguire brevemente l'invio. Un
404sugli endpoint di lettura subito dopo un202significa che il messaggio non è ancora visibile: riprova dopo un momento.
Riprovare in sicurezza
Invia un header Idempotency-Key con un valore univoco per ogni invio logico. Se una richiesta è riuscita ma non hai mai visto la risposta, ripetila con la stessa chiave. L'API restituisce il risultato originale invece di inviare una seconda email e include un header Idempotency-Replay. Idempotenza contiene il formato della chiave e la durata di conservazione.
Invio in batch
Per ridurre le richieste API, POST /v1/email/batches accetta fino a 100 messaggi indipendenti e li valida come un'unica unità. Anche chiamare l'endpoint di invio singolo in un ciclo è supportato. Ogni elemento del batch usa il payload descritto in questa pagina, scheduled_at incluso, quindi un singolo batch può combinare messaggi immediati e programmati.
Costi e fatturazione
Gli invii email vengono conteggiati per destinatario, quindi un messaggio a tre destinatari conta come tre invii. Con il piano Email Free, gli invii vengono conteggiati nella quota mensile e giornaliera del piano. Con un piano Email a pagamento, ogni invio viene addebitato sul tuo wallet alla tariffa del piano per 1.000 email. Fatturazione e utilizzo descrive il modello di conteggio e la lettura dell'utilizzo in tempo reale, e la pagina dei prezzi email elenca la tariffa di ogni piano.
Prossimi passi
- Template email: crea e pubblica i template che invii da qui
- Categorie: come
marketingetransactionalcambiano il comportamento delle soppressioni - Soppressioni: a chi non consegniamo e perché
- Invio programmato: consegna in un momento futuro con
scheduled_at - Sandbox di test: destinatari sandbox e invio prima della verifica
- Riferimento API: gli schemi completi di richiesta e risposta
Risorse correlate
Continua con la documentazione, le guide e gli esempi per questo argomento.