E-mail verzenden
POST /v1/email/messages verzendt één e-mail. Geef een afzender, ontvangers en inhoud mee in een JSON-payload. De API retourneert 202 Accepted met een bericht-ID en bezorgt de e-mail daarna asynchroon. Zie de API-referentie voor de volledige schema's.
Een minimale verzending
De kleinste geldige payload is een from, minstens één to-ontvanger, een subject en een body (html, text of beide). Het from-adres moet op een domein staan dat je in deze werkruimte hebt geverifieerd, of op het onboardingdomein.
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>"
}'Gebruik je regionale host (https://us1.platform.bird.com of https://eu1.platform.bird.com) met een bijbehorende bk_{region}_...-sleutel.
Het verzendvoorbeeld gebruikt delivered@messagebird.dev, een sandbox-adres dat altijd mail accepteert. De API weigert placeholderdomeinen met een 422: example.com, example.net, example.org, example.edu, test.com en alles onder de gereserveerde .test-, .example-, .invalid- of .localhost-TLD's. Een verzending naar deze domeinen kan alleen bouncen, wat je afzenderreputatie schaadt.
Verzenden voordat je een domein verifieert
Tijdens het onboarden kun je verzenden vanaf ons gedeelde onboardingdomein, onboarding@messagebird.dev. Die verzendingen slaan de domeincontrole over, maar bereiken alleen geverifieerde leden van je eigen werkruimte en sandbox-adressen, met een dagelijks ontvangerlimiet. De quickstart bevat de exacte regels en limieten.
De payload opbouwen
Ontvangers
to, cc en bcc accepteren elk maximaal 50 adressen, en to vereist er minstens één. Elke vermelding is een gewone e-mailstring, een RFC 5322-mailboxstring (Jane <jane@acme.com>) of een object met een optionele weergavenaam.
Ontvangers op de suppressielijst van de werkruimte laten het verzoek niet mislukken. Het retourneert nog steeds een 202, en elke onderdrukte ontvanger verschijnt op de leesendpoints als status: rejected met de reden recipient_suppressed, ook wanneer dat voor alle ontvangers van de verzending geldt.
Inhoud
subject is verplicht bij inline verzendingen, maximaal 998 tekens. Geef html, text of beide mee, elk maximaal 524.288 tekens. Stuur waar mogelijk beide mee: een client die geen HTML kan weergeven, valt terug op het tekstgedeelte.
Om inline-inhoud te personaliseren, plaats je {{ variable }}-tokens in het onderwerp of de body en geef je hun waarden mee in parameters, maximaal 16 KB geserialiseerd. Eén set waarden geldt voor alle ontvangers van de verzending, en een token zonder bijbehorende sleutel wordt leeg weergegeven. Verzend voor inhoud die je hergebruikt in plaats daarvan een template.
Neem parameters op, zelfs als leeg object ({}), om het onderwerp en de body als Liquid te verwerken. Laat het weg om tokens zoals {{ animal }} letterlijk te verzenden. Elke parameternaam is één woord, zoals first_name; namen met punten en de gereserveerde naam bird worden geweigerd. Ongeldige Liquid-syntax en niet-ondersteunde tags of filters retourneren 422.
Waarden die in HTML worden ingevoegd, worden ge-escaped zodat ze de omringende markup niet kunnen wijzigen. Gebruik voor een volledige link- of afbeeldings-URL {{ link }} zonder url_encode. Codeer een waarde binnen een URL-query expliciet, bijvoorbeeld https://example.com/search?q={{ query | url_encode }}.
Reply-to en aangepaste headers
reply_to accepteert 1 tot 25 adressen, in dezelfde formaten als ontvangers. Elk antwoord van een ontvanger gaat naar al deze adressen, dus meestal gebruik je er één of twee.
headers is een string-naar-string-object voor je eigen headers, bijvoorbeeld {"X-Campaign": "spring-2026"}, met een maximum van 25 headers en waarden tot 998 tekens. Twee soorten headers leveren een 422 op:
- Adresserings- en platformheaders. Stel de adressering van het bericht in via de specifieke velden (
from,to,cc,bcc,reply_to,subject). Die namen, en de headers die wij voor je genereren (Content-Type,Content-Transfer-Encoding,DKIM-Signature,Received,Return-Path), kun je hier niet instellen. - Elke waarde met een carriage return of line feed.
Transactionele verzendingen accepteren List-Unsubscribe en List-Unsubscribe-Post. Marketingverzendingen gebruiken standaard uitschrijving die door Bird wordt beheerd.
Tracking
track_opens en track_clicks staan beide standaard op true. Zet een van beide op false om open-pixelinjectie of linkherschrijving voor deze verzending over te slaan. Deze vlaggen laten trackingpixels en links die al in je ingediende inhoud staan ongewijzigd. Tracking en statistieken beschrijft wat elk van beide in het bericht wijzigt.
Categorie en IP-pool
category classificeert de inhoud en bepaalt het suppressiebeleid: marketing blokkeert bezorging bij elke suppressiereden en bij elke opt-out, en transactional bezorgt ondanks een klachtsuppressie of een opt-out die alleen voor marketing geldt (een opt-out voor alle berichten blokkeert ook). De standaard is de categorie van het template bij een templateverzending en anders marketing, dus stel transactional expliciet in voor bonnetjes, wachtwoordresets en andere operationele e-mail. Categorieën behandelt de keuze. Mail die via SMTP wordt ingediend, haalt de categorie in plaats daarvan uit de SMTP-configuratie van de sleutel.
ip_pool_id kiest de verzendpool: een pool-ID (ipp_...), of ipp_shared om expliciet via de gedeelde pool te routeren. Laat het weg voor de standaardpool van je organisatie. Een onbekende pool, of een pool zonder beschikbare dedicated IP's om vanaf te verzenden, wordt geweigerd met een 422.
Veldreferentie
| Veld | Type | Verplicht | Limieten en opmerkingen |
|---|---|---|---|
from | address | ja | Moet op een geverifieerd domein staan, of op het onboardingdomein |
to | address[] | ja | 1 tot 50 |
cc, bcc | address[] | nee | Maximaal 50 elk |
subject | string | inline verzendingen | Maximaal 998 tekens; weglaten bij templateverzendingen |
html, text | string | minstens één | Maximaal 524.288 tekens elk; weglaten bij templateverzendingen |
reply_to | address[] | nee | 1 tot 25; antwoorden gaan naar elk vermeld adres |
headers | object (string → string) | nee | Maximaal 25; gereserveerde namen worden geweigerd (zie aangepaste headers) |
parameters | object | nee | Waarden voor {{ tokens }} in inline-inhoud; maximaal 16 KB geserialiseerd; gedeeld door alle ontvangers |
tags | {name, value}[] | nee | Maximaal 20; naam ≤ 32 tekens, waarde ≤ 64 tekens; alleen [A-Za-z0-9_-]; namen uniek per verzending |
metadata | object | nee | Willekeurige JSON, maximaal 2 KB geserialiseerd |
reference | string | nee | 1–255 tekens; exacte match voor ?reference= in de berichtenlijst |
track_opens | boolean | nee | Standaard true |
track_clicks | boolean | nee | Standaard true |
category | string | nee | marketing of transactional; standaard die van het template bij een templateverzending, anders marketing |
ip_pool_id | string | nee | ipp_... of ipp_shared; weglaten voor de standaardpool van je organisatie |
template | object | nee | Verzend een gepubliceerd template op id of slug, met parameters voor de variabelen en een optionele language |
attachments | object[] | nee | Maximaal 20; zie bijlagen |
scheduled_at | RFC 3339-timestamp | nee | Plan inline-inhoud of een template in; zie gepland verzenden |
Verzenden met een template
Templates kunnen bird.contact.*-waarden gebruiken uit het contact dat bij elk To-adres hoort. Cc- en Bcc-ontvangers krijgen een kopie van de gepersonaliseerde e-mail van elke To-ontvanger. Dit werkt ook in de batch-API en bij geplande verzendingen; zie personaliseren met variabelen voor een voorbeeld met meerdere ontvangers.
Verzend in plaats van inline-inhoud een gepubliceerd template: stel template in als een object dat het template benoemt op id (emt_...) of op slug, precies een van de twee, met de variabelewaarden in template.parameters. Laat subject, html en text weg, want het template bevat die al.
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" }
}
}'De inhoud van een template is Liquid, dus naast gewone {{ variable }}-substitutie kun je filters, {% if %}-condities en {% for %}-loops gebruiken. Personaliseren met variabelen beschrijft de constructies die een publicatie weigert. In template.parameters plaats je de waarden voor de eigen parameters van het template, op naam. Laat er een weg en de verzending wordt geweigerd met een 422 die de ontbrekende parameter benoemt. De verzending ondersteunt dezelfde tags, metadata, tracking en bijlagen als inline e-mail. Wat specifiek is voor een templateverzending:
- Inline of met template, nooit beide.
templateverzenden samen metsubject,htmloftextwordt geweigerd met een422. De API weigert ook variabelewaarden in het toplevelparameters-veld; bij een templateverzending horen ze intemplate.parameters. birdis de enige gereserveerde naam. Een placeholderpad dat begint metbird.verwijst naar onze eigen data, zoals de uitschrijflink of het contactrecord van de ontvanger, dus eentemplate.parameters-sleutel mag nietbirdheten. Elke andere sleutel definieer je zelf, en elke sleutel is één woord:{"order_number": "A-1043"}vult{{ order_number }}.- Een template kan nu of later verzenden. Voeg
scheduled_attoe om het verzenden in te plannen. We slaan bij acceptatie de gepubliceerde versie, de geselecteerde taal, de parameters van de aanroeper en de contactwaarden op, zodat latere wijzigingen aan contacten of fallbacks geaccepteerde berichten niet veranderen. Als je het template verwijdert vóór het verzendtijdstip, wordt het bericht geweigerd metgeneration_failure. - Personaliseer met contactkenmerken. Zie contactvereisten en standaardbegroetingen.
- Contactmatching negeert weergavenamen. E-mailadressen worden omgezet naar kleine letters, met behoud van punten en plustags. Delen meerdere contacten dat adres, dan retourneert de verzending
EmailTemplateContactAmbiguous; werk de dubbele records bij voordat je het opnieuw probeert. - Verzenden gebruikt e-mailrechten. Een sleutel met
emails:writekan een gepubliceerd template verzenden zonder rechten om contacten te zoeken.emails:readgeeft toegang tot de niet-gevoelige inhoud van het resulterende bericht. Voor het zoeken van contacten en eigenschappen blijven de bestaande rechten gelden. - Een verzending gebruikt de gepubliceerde versie van het template. Concepten worden nooit verzonden. Een onbekend template wordt geweigerd met een
404, en een template zonder gepubliceerde versie met een422. languagekiest een van de talen van het template. Laat het weg om de standaardtaal van het template te verzenden. Vraag een taal aan die het template niet heeft, en de eigenon_missing_language-instelling bepaalt of de dichtstbijzijnde match wordt verzonden of de verzending wordt geweigerd. Een template datlanguage_source_requiredinstelt, weigert een verzending die helemaal geen taal benoemt.- De categorie van het template is een standaard, en de jouwe overschrijft die. Laat
categoryweg en de verzending erft die van het template, dus een transactioneel template hoeft die niet bij elke aanroep herhaald.
E-mailtemplates behandelt het maken, publiceren en de constructies die een template kan bevatten.
Inhoud voor een ontvanger lezen
De berichtgeschiedenis van templates gebruikt de contactwaarden die zijn opgeslagen toen de verzending werd geaccepteerd, ook als je het contact later wijzigt of verwijdert. Elke gekopieerde bezorging heeft een eigen ontvanger-ID.
Vraag de lijst met ontvangers van het bericht op om hun ID's te krijgen en geef daarna recipient_id mee aan Get stored message content. Een nieuw templatebericht met snapshots van meerdere ontvangers retourneert 422 als die selector ontbreekt. Bij één ontvanger wordt die automatisch geselecteerd. Een ontvanger die bij een ander bericht hoort, retourneert 404.
GET- en lijstantwoorden voor berichten gebruiken het geaccepteerde onderwerp van de eerste To-ontvanger, aangeduid met subject_recipient_id. De ontvangerslijst geeft voor elke ontvanger het eigen onderwerp. Deze onderwerpen blijven beschikbaar wanneer opslag van de body is uitgeschakeld. Reads van inhoud volgen nog steeds de opslag- en bewaarinstellingen van het bericht, met 404 als de inhoud niet is opgeslagen, 425 zolang die in behandeling is en 410 na het verlopen.
Als een bewaarde snapshot van een ontvanger niet meer beschikbaar is, blijven de berichtstatus en het representatieve onderwerp leesbaar, maar ontbreken de onderwerpen per ontvanger en is de inhoud niet beschikbaar. Reads zoeken geen actuele contactwaarden op om een eerdere verzending te reconstrueren.
Tags vs metadata
Hiermee koppel je je eigen data aan een verzending. Ze verschillen in hoe je die data later opvraagt:
tagszijn gestructureerde{name, value}-paren: maximaal 20 per verzending, naam maximaal 32 tekens, waarde maximaal 64, alleen ASCII-letters, cijfers, underscore en koppelteken, en namen uniek binnen de verzending. Tags zijn filterdimensies, dus je kunt de berichtenlijst filteren op tag en analytics en dashboardoverzichten uitsplitsen op tag. Gebruik ze voor labels met lage kardinaliteit zoalscampaign,experiment_variantofsource.metadatais een willekeurig JSON-object, maximaal 2 KB geserialiseerd. We slaan het op, retourneren het bij API-reads en sturen het mee bij elk webhook-event, dus het is geschikt voor context die je teruggestuurd wilt krijgen: interne ID's, foreign keys, gestructureerde payloads.referenceis één string van 1 tot 255 tekens waarop je het bericht wilt terugvinden, zoals een order- of ticketnummer.GET /v1/email/messages?reference=zoekt er exact en hoofdlettergevoelig op, en meerdere berichten kunnen dezelfde waarde hebben. De waarde wordt geretourneerd bij reads en meegestuurd bij de webhook-events van het bericht. Stel deze alleen in als je erop wilt zoeken, en zet andere data inmetadata.
Elk webhook-event bevat de tags en metadata samen met de correlatie-ID's (email_id, recipient_id), zodat je kunt afstemmen met je eigen administratie zonder een extra lookup. Tagnamen en toplevel metadatasleutels die beginnen met __bird worden geweigerd. Je hoeft device, geografie, mailboxprovider, bouncetype of ontvangersdomein niet in een van beide velden te coderen, want we registreren elk daarvan al als een analytics-dimensie.
{
"tags": [{ "name": "campaign", "value": "onboarding" }],
"metadata": { "user_id": "usr_12345", "order_id": "ord_98765" },
"reference": "ord_98765"
}Bijlagen
attachments accepteert maximaal 20 bestanden per bericht, als base64-gecodeerde bytes inline. We weigeren een verzending waarvan de geschatte berichtgrootte na base64-codering boven de 20 MB komt, dus houd de ruwe bijlage-inhoud op of onder 15 MB voor speelruimte. Bijlagen beschrijft het veldcontract, inline-afbeeldingen, de geblokkeerde bestandstypen en hoe je een bijlage terugdownloadt.
Wat een 202 betekent
Een geslaagde verzending retourneert 202 Accepted met een bericht-ID met het voorvoegsel em_ en 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"
}De 202 betekent dat we de verzending duurzaam hebben geaccepteerd. Fouten die je kunt oplossen komen terug op het verzoek zelf als een 422: een niet-geverifieerd afzenderdomein of een veld dat niet valideert. Uitkomsten per ontvanger (afgeleverd, gebounced, uitgesteld, klacht) komen daarna binnen via webhooks en de bericht-read-endpoints.
Daar volgen twee dingen uit:
- Reads retourneren status zonder de body.
GET /v1/email/messages/{message_id}retourneert bericht- en ontvangerstatus, nooit dehtml- oftext-body. Wanneer contentopslag is ingeschakeld voor de werkruimte, blijven opgeslagen body's tot 30 dagen beschikbaar viaGET /v1/email/messages/{message_id}/content. - Een read kan kort achterlopen op de verzending. Een
404op de read-endpoints direct na een202betekent dat het bericht nog niet zichtbaar is; probeer het even later opnieuw.
Veilig opnieuw proberen
Stuur een Idempotency-Key-header mee met een unieke waarde per logische verzending. Als een verzoek is geslaagd maar je het antwoord nooit hebt gezien, herhaal het dan met dezelfde sleutel. De API retourneert het oorspronkelijke resultaat in plaats van een tweede e-mail te versturen, en bevat een Idempotency-Replay-header. Idempotency beschrijft het sleutelformaat en de bewaartermijn.
Batchverzending
Om het aantal API-verzoeken te verminderen, accepteert POST /v1/email/batches tot 100 onafhankelijke berichten en valideert ze als één geheel. Het single-send-endpoint in een lus aanroepen wordt ook ondersteund. Een batch-item gebruikt de payload op deze pagina, inclusief scheduled_at, dus één batch kan directe en geplande berichten combineren.
Kosten en facturering
E-mailverzendingen worden per ontvanger geteld, dus een bericht aan drie ontvangers telt als drie verzendingen. Met het Email Free-abonnement tellen verzendingen mee voor de maandelijkse en dagelijkse limiet. Met een betaald Email-abonnement wordt elke verzending van je wallet afgeschreven tegen het tarief van het abonnement per 1.000 e-mails. Billing and usage beschrijft het telmodel en de actuele verbruiksweergave, en de prijspagina voor e-mail vermeldt het tarief van elk abonnement.
Volgende stappen
- E-mailtemplates: maak en publiceer de templates die je hier verstuurt
- Categorieën: hoe
marketingentransactionalhet suppressiegedrag veranderen - Suppressies: aan wie we niet bezorgen, en waarom
- Geplande verzending: bezorg op een toekomstig tijdstip met
scheduled_at - Testsandbox: sandbox-ontvangers en verzenden vóór domeinverificatie
- API-referentie: de volledige request- en responseschema's
Gerelateerde bronnen
Ga verder met de documentatie, handleidingen en voorbeelden voor dit onderwerp.