Wysyłanie e-maili
POST /v1/email/messages wysyła jeden e-mail. Podaj nadawcę, odbiorców i treść w payloadzie JSON. API zwraca 202 Accepted z identyfikatorem wiadomości, a następnie dostarcza e-mail asynchronicznie. Pełne schematy znajdziesz w dokumentacji API.
Minimalne wysłanie
Najmniejszy prawidłowy payload to from, co najmniej jeden odbiorca to, subject i treść (html, text lub oba). Adres from musi znajdować się w domenie zweryfikowanej w tym obszarze roboczym lub w domenie onboardingowej.
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>"
}'Użyj swojego regionalnego hosta (https://us1.platform.bird.com lub https://eu1.platform.bird.com) z pasującym kluczem bk_{region}_....
Przykład wysyłania używa delivered@messagebird.dev, adresu sandboxowego, który zawsze przyjmuje pocztę. API odrzuca domeny zastępcze z błędem 422: example.com, example.net, example.org, example.edu, test.com oraz wszystko w zarezerwowanych TLD .test, .example, .invalid i .localhost. Wysyłka na te domeny może jedynie zostać odrzucona (bounce), co kosztuje Cię reputację nadawcy.
Wysyłanie przed zweryfikowaniem domeny
Podczas onboardingu możesz wysyłać z naszej współdzielonej domeny onboardingowej onboarding@messagebird.dev. Te wysyłki pomijają sprawdzanie domeny, ale docierają tylko do zweryfikowanych członków Twojego obszaru roboczego i adresów sandboxowych, z dziennym limitem odbiorców. Quickstart zawiera dokładne zasady i limity.
Budowanie payloadu
Odbiorcy
to, cc i bcc przyjmują po maksymalnie 50 adresów, a to wymaga co najmniej jednego. Każdy wpis to zwykły ciąg z adresem e-mail, ciąg skrzynki pocztowej RFC 5322 (Jane <jane@acme.com>) lub obiekt z opcjonalną nazwą wyświetlaną.
Odbiorcy znajdujący się na liście suppresji obszaru roboczego nie powodują błędu żądania. Żądanie nadal zwraca 202, a każdy odbiorca objęty suppresją pojawia się w endpointach odczytu jako status: rejected z powodem recipient_suppressed, również wtedy, gdy dotyczy to wszystkich odbiorców wysyłki.
Treść
subject jest wymagany w wysyłkach inline i może mieć do 998 znaków. Podaj html, text lub oba, każdy do 524 288 znaków. Wysyłaj oba, gdy to możliwe: klient, który nie potrafi renderować HTML, wyświetli wtedy część tekstową.
Aby spersonalizować treść inline, umieść tokeny {{ variable }} w temacie lub treści i przekaż ich wartości w parameters, do 16 KB po serializacji. Jeden zestaw wartości obejmuje wszystkich odbiorców wysyłki, a token bez pasującego klucza renderuje się jako pusty. Dla treści wielokrotnego użytku wyślij zamiast tego szablon.
Dodaj parameters, nawet jako pusty obiekt ({}), aby temat i treść zostały przetworzone jako Liquid. Pomiń go, aby wysłać tokeny takie jak {{ animal }} dokładnie w zapisanej postaci. Każda nazwa parametru to pojedyncze słowo, np. first_name; nazwy z kropkami i zarezerwowana nazwa bird są odrzucane. Nieprawidłowa składnia Liquid oraz nieobsługiwane tagi lub filtry zwracają 422.
Wartości wstawiane do HTML są escapowane, aby nie mogły zmienić otaczającego kodu znaczników. Dla pełnego adresu URL linku lub obrazu użyj {{ link }} bez url_encode. Wartość umieszczaną w zapytaniu URL zakoduj jawnie, na przykład https://example.com/search?q={{ query | url_encode }}.
Reply-to i niestandardowe nagłówki
reply_to przyjmuje od 1 do 25 adresów w tych samych formatach co odbiorcy. Każda odpowiedź odbiorcy trafia do wszystkich tych adresów, więc zwykle podaje się jeden lub dwa.
headers to obiekt typu string-to-string na Twoje własne nagłówki, na przykład {"X-Campaign": "spring-2026"}, z limitem 25 nagłówków o wartościach do 998 znaków. Dwa rodzaje nagłówków powodują zwrócenie 422:
- Nagłówki adresowe i platformowe. Ustaw adresowanie wiadomości przez dedykowane pola (
from,to,cc,bcc,reply_to,subject). Tych nazw ani nagłówków, które generujemy za Ciebie (Content-Type,Content-Transfer-Encoding,DKIM-Signature,Received,Return-Path), nie można ustawiać tutaj. - Dowolna wartość zawierająca znak powrotu karetki lub nowej linii.
Wysyłki transakcyjne akceptują List-Unsubscribe i List-Unsubscribe-Post. Wysyłki marketingowe domyślnie korzystają z rezygnacji zarządzanej przez Bird.
Śledzenie
track_opens i track_clicks domyślnie mają wartość true. Ustaw dowolne z nich na false, aby pominąć wstrzykiwanie piksela otwarcia lub przepisywanie linków w tej wysyłce. Te flagi nie zmieniają pikseli śledzących ani linków, które już znajdują się w przesłanej treści. Śledzenie i metryki opisuje, co każda z nich zmienia w wiadomości.
Kategoria i pula IP
category klasyfikuje treść i ustawia politykę suppresji: marketing blokuje dostarczanie przy każdym powodzie suppresji i każdej rezygnacji, a transactional dostarcza mimo suppresji ze skargi lub rezygnacji tylko z marketingu (rezygnacja zarejestrowana dla wszystkich wiadomości również blokuje). Domyślnie przyjmuje kategorię szablonu w wysyłce z szablonem, a w pozostałych przypadkach marketing, więc ustaw transactional jawnie dla potwierdzeń, resetów haseł i innej poczty operacyjnej. Kategorie opisują wybór. Poczta przesłana przez SMTP pobiera natomiast kategorię z konfiguracji SMTP klucza.
ip_pool_id wybiera pulę wysyłkową: identyfikator puli (ipp_...) lub ipp_shared, aby jawnie kierować przez pulę współdzieloną. Pomiń go, aby użyć domyślnej puli organizacji. Nieznana pula lub pula bez dostępnych dedykowanych adresów IP jest odrzucana z błędem 422.
Opis pól
| Pole | Typ | Wymagane | Limity i uwagi |
|---|---|---|---|
from | address | tak | Musi być w zweryfikowanej domenie lub w domenie onboardingowej |
to | address[] | tak | Od 1 do 50 |
cc, bcc | address[] | nie | Do 50 każde |
subject | string | wysyłki inline | Do 998 znaków; pomiń w wysyłkach z szablonem |
html, text | string | co najmniej jedno | Do 524 288 znaków każde; pomiń w wysyłkach z szablonem |
reply_to | address[] | nie | Od 1 do 25; odpowiedzi trafiają do każdego wymienionego adresu |
headers | object (string → string) | nie | Do 25; zarezerwowane nazwy są odrzucane (zobacz niestandardowe nagłówki) |
parameters | object | nie | Wartości dla {{ tokens }} w treści inline; do 16 KB po serializacji; wspólne dla wszystkich odbiorców |
tags | {name, value}[] | nie | Do 20; nazwa ≤ 32 znaki, wartość ≤ 64 znaki; tylko [A-Za-z0-9_-]; nazwy unikalne w wysyłce |
metadata | object | nie | Dowolny JSON, do 2 KB po serializacji |
reference | string | nie | Od 1 do 255 znaków; dokładne dopasowanie dla ?reference= na liście wiadomości |
track_opens | boolean | nie | Domyślnie true |
track_clicks | boolean | nie | Domyślnie true |
category | string | nie | marketing lub transactional; domyślnie kategoria szablonu w wysyłce z szablonem, w przeciwnym razie marketing |
ip_pool_id | string | nie | ipp_... lub ipp_shared; pomiń, aby użyć domyślnej puli organizacji |
template | object | nie | Wyślij opublikowany szablon po id lub slug, z parameters dla jego zmiennych i opcjonalnym language |
attachments | object[] | nie | Do 20; zobacz załączniki |
scheduled_at | znacznik czasu RFC 3339 | nie | Zaplanuj wysyłkę treści inline lub template; zobacz planowanie wysyłki |
Wysyłanie z szablonem
Szablony mogą używać wartości bird.contact.* z kontaktu pasującego do każdego adresu To. Odbiorcy Cc i Bcc otrzymują kopię spersonalizowanego e-maila każdego odbiorcy To. Działa to również we wsadowym API i w wysyłkach zaplanowanych; przykład z wieloma odbiorcami znajdziesz w sekcji personalizacja za pomocą zmiennych.
Zamiast treści inline wyślij opublikowany szablon: ustaw template na obiekt wskazujący go po id (emt_...) lub po slug, dokładnie jedno z dwóch, z wartościami zmiennych w template.parameters. Pomiń subject, html i text, ponieważ szablon już je zawiera.
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" }
}
}'Treść szablonu to Liquid, więc oprócz zwykłego podstawiania {{ variable }} może korzystać z filtrów, instrukcji warunkowych {% if %} i pętli {% for %}. Personalizacja za pomocą zmiennych wymienia kilka konstrukcji, które publikacja odrzuca. W template.parameters podajesz wartości własnych parametrów szablonu, według nazwy. Jeśli pominiesz którąś z nich, wysyłka zostanie odrzucona z błędem 422 wskazującym jej nazwę. Wysyłka obsługuje te same tags, metadata, śledzenie i załączniki co e-mail inline. Co jest specyficzne dla wysyłki z szablonem:
- Inline albo szablon, nigdy oba. Wysłanie
templaterazem zsubject,htmllubtextjest odrzucane z błędem422. API odrzuca również wartości zmiennych w poluparametersnajwyższego poziomu; w wysyłce z szablonem należy je umieścić wtemplate.parameters. birdto jedyna zarezerwowana nazwa. Ścieżka placeholdera zaczynająca się odbird.wskazuje nasze własne dane, takie jak link rezygnacji czy rekord kontaktu odbiorcy, więc klucztemplate.parametersnie może nosić nazwybird. Każdy inny klucz możesz zdefiniować sam, a każdy jest prostym pojedynczym słowem:{"order_number": "A-1043"}wypełnia{{ order_number }}.- Szablon można wysłać teraz lub później. Dodaj
scheduled_at, aby zaplanować wysyłkę. W momencie przyjęcia zapisujemy opublikowaną wersję, wybrany język, parametry przekazane przez wywołującego i wartości kontaktów, więc późniejsze zmiany kontaktów lub wartości zastępczych nie zmieniają przyjętych wiadomości. Jeśli usuniesz szablon przed zaplanowanym czasem wysyłki, wiadomość zostanie odrzucona z błędemgeneration_failure. - Personalizuj za pomocą atrybutów kontaktu. Zobacz wymagania dotyczące kontaktów i domyślne powitania.
- Dopasowanie kontaktu ignoruje nazwy wyświetlane. Adresy e-mail są zapisywane małymi literami, z zachowaniem kropek i tagów z plusem. Jeśli kilka kontaktów ma ten sam adres, wysyłka zwraca
EmailTemplateContactAmbiguous; zaktualizuj zduplikowane rekordy przed ponowną próbą. - Wysyłka korzysta z uprawnień e-mail. Klucz z
emails:writemoże wysłać opublikowany szablon bez uprawnienia do wyszukiwania kontaktów.emails:readdaje dostęp do niewrażliwej treści wynikowej wiadomości. Wyszukiwanie kontaktów i właściwości zachowuje dotychczasowe uprawnienia. - Wysyłka korzysta z opublikowanej wersji szablonu. Wersje robocze nigdy nie są wysyłane. Nieznany szablon jest odrzucany z błędem
404, a szablon bez opublikowanej wersji z błędem422. languagewybiera jeden z języków szablonu. Pomiń go, aby wysłać domyślny język szablonu. Jeśli poprosisz o język, którego szablon nie posiada, jego własne ustawienieon_missing_languagedecyduje, czy zamiast tego zostanie wysłane najbliższe dopasowanie, czy wysyłka zostanie odrzucona. Szablon z ustawieniemlanguage_source_requiredodrzuca wysyłkę, która nie wskazuje żadnego języka.- Kategoria szablonu jest wartością domyślną, a Twoja ją nadpisuje. Pomiń
category, a wysyłka odziedziczy kategorię szablonu, więc szablon transakcyjny nie wymaga powtarzania jej przy każdym wywołaniu.
Szablony e-mail opisują tworzenie, publikowanie i konstrukcje, jakie szablon może zawierać.
Odczyt treści dla odbiorcy
Historia wiadomości z szablonu używa wartości kontaktów zapisanych w momencie przyjęcia wysyłki, nawet jeśli później edytujesz lub usuniesz kontakt. Każde skopiowane dostarczenie ma własny identyfikator odbiorcy.
Pobierz listę odbiorców wiadomości, aby uzyskać ich identyfikatory, a następnie przekaż recipient_id do Get stored message content. Nowa wiadomość z szablonu z wieloma migawkami odbiorców zwraca 422, jeśli nie podasz tego selektora. Pojedynczy odbiorca jest wybierany automatycznie; odbiorca należący do innej wiadomości zwraca 404.
Odpowiedzi GET i listy wiadomości zawierają przyjęty temat dla pierwszego odbiorcy To, wskazanego przez subject_recipient_id. Lista odbiorców podaje własny temat każdego odbiorcy. Tematy te pozostają dostępne, gdy przechowywanie treści jest wyłączone. Odczyty treści nadal podlegają ustawieniom przechowywania i retencji wiadomości, w tym 404, gdy treść nie jest przechowywana, 425 w trakcie przetwarzania i 410 po wygaśnięciu.
Jeśli zachowana migawka odbiorcy stanie się niedostępna, status wiadomości i reprezentatywny temat nadal można odczytać, ale tematy odbiorców są pomijane, a treść jest niedostępna. Odczyty nie pobierają bieżących wartości kontaktów, aby odtworzyć wcześniejszą wysyłkę.
Tagi a metadane
Te pola dołączają Twoje własne dane do wysyłki i różnią się sposobem późniejszego odpytywania:
tagsto strukturalne pary{name, value}: do 20 na wysyłkę, nazwa do 32 znaków, wartość do 64, tylko litery ASCII, cyfry, podkreślnik i myślnik, nazwy unikalne w ramach wysyłki. Tagi są wymiarami filtrowania, więc możesz filtrować listę wiadomości po tagu oraz dzielić analitykę i podsumowania dashboardu po tagu. Używaj ich dla etykiet o niskiej kardynalności, takich jakcampaign,experiment_variantczysource.metadatato dowolny obiekt JSON, do 2 KB po serializacji. Przechowujemy go, zwracamy przy odczytach API i odsyłamy w każdym zdarzeniu webhooka, więc nadaje się do kontekstu, który chcesz odzyskać: wewnętrzne ID, klucze obce, strukturalne payloady.referenceto jeden ciąg od 1 do 255 znaków, po którym chcesz wyszukać wiadomość, np. numer zamówienia lub zgłoszenia.GET /v1/email/messages?reference=dopasowuje go dokładnie, z uwzględnieniem wielkości liter, a kilka wiadomości może mieć tę samą wartość. Jest zwracany przy odczytach i odsyłany w zdarzeniach webhooka wiadomości. Ustawiaj go tylko wtedy, gdy musisz po nim wyszukiwać, a pozostałe dane umieszczaj wmetadata.
Każde zdarzenie webhooka zawiera tagi i metadane razem z identyfikatorami korelacji (email_id, recipient_id), więc możesz uzgodnić dane ze swoimi rekordami bez dodatkowego odczytu. Nazwy tagów i klucze metadanych najwyższego poziomu zaczynające się od __bird są odrzucane. Nie musisz kodować urządzenia, geografii, dostawcy skrzynki, typu odrzucenia ani domeny odbiorcy w żadnym z tych pól, ponieważ każdy z nich rejestrujemy już jako wymiar analityki.
{
"tags": [{ "name": "campaign", "value": "onboarding" }],
"metadata": { "user_id": "usr_12345", "order_id": "ord_98765" },
"reference": "ord_98765"
}Załączniki
attachments przyjmuje do 20 plików na wiadomość jako bajty zakodowane w base64. Odrzucamy wysyłkę, której szacowany rozmiar wygenerowanej wiadomości przekracza 20 MB (mierzony po kodowaniu base64), więc utrzymuj surową zawartość załączników na poziomie 15 MB lub mniej, jako zapas. Załączniki zawierają kontrakt pól, obrazy inline, zablokowane typy plików i sposób pobierania załącznika.
Co oznacza 202
Udana wysyłka zwraca 202 Accepted z identyfikatorem wiadomości z prefiksem em_ i 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"
}202 oznacza, że trwale przyjęliśmy wysyłkę. Błędy, które możesz naprawić, wracają w samym żądaniu jako 422: niezweryfikowana domena nadawcy lub pole, które nie przechodzi walidacji. Wyniki per odbiorca (dostarczono, odrzucono, odroczono, zgłoszono skargę) docierają potem przez webhooki i endpointy odczytu wiadomości.
Wynikają z tego dwie rzeczy:
- Odczyty zwracają stan bez treści.
GET /v1/email/messages/{message_id}zwraca stan wiadomości i odbiorcy, nigdy treśćhtmlanitext. Gdy przechowywanie treści jest włączone dla obszaru roboczego, zapisane treści pozostają dostępne przez maksymalnie 30 dni odGET /v1/email/messages/{message_id}/content. - Odczyt może chwilowo nie nadążać za wysyłką.
404na endpointach odczytu tuż po202oznacza, że wiadomość nie jest jeszcze widoczna, więc spróbuj ponownie za chwilę.
Bezpieczne ponawianie
Wyślij nagłówek Idempotency-Key z unikalną wartością dla każdej logicznej wysyłki. Jeśli żądanie się powiodło, ale nie otrzymałeś odpowiedzi, wyślij je ponownie z tym samym kluczem. API zwraca oryginalny wynik zamiast wysyłać drugi e-mail i dołącza nagłówek Idempotency-Replay. Idempotentność opisuje format klucza i czas przechowywania.
Wysyłanie wsadowe
Aby zmniejszyć liczbę żądań API, POST /v1/email/batches przyjmuje do 100 niezależnych wiadomości i waliduje je jako jedną całość. Wywoływanie endpointu pojedynczej wysyłki w pętli również jest obsługiwane. Element wsadu używa payloadu opisanego na tej stronie, włącznie z scheduled_at, więc jeden wsad może łączyć wiadomości natychmiastowe i zaplanowane.
Koszty i rozliczenia
Wysyłki e-mail są mierzone per odbiorca, więc wiadomość do trzech odbiorców liczy się jako trzy wysyłki. W planie Email Free wysyłki są odliczane od jego miesięcznego i dziennego limitu. W płatnym planie Email każda wysyłka jest pobierana z Twojego portfela według stawki planu za 1000 e-maili. Rozliczenia i zużycie opisują model mierzenia i odczyt bieżącego zużycia, a strona z cennikiem e-mail podaje stawkę każdego planu.
Następne kroki
- Szablony e-mail: twórz i publikuj szablony, które tu wysyłasz
- Kategorie: jak
marketingitransactionalzmieniają działanie suppresji - Suppresje: do kogo nie dostarczamy i dlaczego
- Wysyłka zaplanowana: dostarcz w przyszłości za pomocą
scheduled_at - Sandbox testowy: odbiorcy sandboxowi i wysyłanie przed weryfikacją
- Dokumentacja API: pełne schematy żądania i odpowiedzi
Powiązane zasoby
Przejdź do dokumentacji, przewodników i przykładów dotyczących tego tematu.