Sign inGet Started

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"

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

VeldTypeVerplichtLimieten en opmerkingen
fromaddressjaMoet op een geverifieerd domein staan, of op het onboardingdomein
toaddress[]ja1 tot 50
cc, bccaddress[]neeMaximaal 50 elk
subjectstringinline verzendingenMaximaal 998 tekens; weglaten bij templateverzendingen
html, textstringminstens éénMaximaal 524.288 tekens elk; weglaten bij templateverzendingen
reply_toaddress[]nee1 tot 25; antwoorden gaan naar elk vermeld adres
headersobject (string → string)neeMaximaal 25; gereserveerde namen worden geweigerd (zie aangepaste headers)
parametersobjectneeWaarden voor {{ tokens }} in inline-inhoud; maximaal 16 KB geserialiseerd; gedeeld door alle ontvangers
tags{name, value}[]neeMaximaal 20; naam ≤ 32 tekens, waarde ≤ 64 tekens; alleen [A-Za-z0-9_-]; namen uniek per verzending
metadataobjectneeWillekeurige JSON, maximaal 2 KB geserialiseerd
referencestringnee1–255 tekens; exacte match voor ?reference= in de berichtenlijst
track_opensbooleanneeStandaard true
track_clicksbooleanneeStandaard true
categorystringneemarketing of transactional; standaard die van het template bij een templateverzending, anders marketing
ip_pool_idstringneeipp_... of ipp_shared; weglaten voor de standaardpool van je organisatie
templateobjectneeVerzend een gepubliceerd template op id of slug, met parameters voor de variabelen en een optionele language
attachmentsobject[]neeMaximaal 20; zie bijlagen
scheduled_atRFC 3339-timestampneePlan 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);

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. template verzenden samen met subject, html of text wordt geweigerd met een 422. De API weigert ook variabelewaarden in het toplevel parameters-veld; bij een templateverzending horen ze in template.parameters.
  • bird is de enige gereserveerde naam. Een placeholderpad dat begint met bird. verwijst naar onze eigen data, zoals de uitschrijflink of het contactrecord van de ontvanger, dus een template.parameters-sleutel mag niet bird heten. 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_at toe 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 met generation_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:write kan een gepubliceerd template verzenden zonder rechten om contacten te zoeken. emails:read geeft 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 een 422.
  • language kiest 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 eigen on_missing_language-instelling bepaalt of de dichtstbijzijnde match wordt verzonden of de verzending wordt geweigerd. Een template dat language_source_required instelt, weigert een verzending die helemaal geen taal benoemt.
  • De categorie van het template is een standaard, en de jouwe overschrijft die. Laat category weg 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:

  • tags zijn 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 zoals campaign, experiment_variant of source.
  • metadata is 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.
  • reference is éé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 in metadata.

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.

Codevoorbeeld
{
  "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:

Codevoorbeeld
{
  "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 de html- of text-body. Wanneer contentopslag is ingeschakeld voor de werkruimte, blijven opgeslagen body's tot 30 dagen beschikbaar via GET /v1/email/messages/{message_id}/content.
  • Een read kan kort achterlopen op de verzending. Een 404 op de read-endpoints direct na een 202 betekent 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

Ga verder met de documentatie, handleidingen en voorbeelden voor dit onderwerp.