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ą do 50 adresów, a to wymaga co najmniej jednego. Każdy wpis to zwykły ciąg 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 suppressions obszaru roboczego nie powodują błędu żądania. Nadal zwracany jest 202, a każdy wstrzymany odbiorca 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 dla wysyłek inline, 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, korzysta z części tekstowej.
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 szablon.
Dodaj parameters, nawet jako pusty obiekt ({}), aby przetworzyć temat i treść jako Liquid. Pomiń go, aby wysłać tokeny takie jak {{ animal }} dokładnie tak, jak zostały zapisane. 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 znacznika. Dla pełnego linku lub URL-a obrazu użyj {{ link }} bez url_encode. Dla wartości wewnątrz zapytania URL zakoduj tę wartość 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 z nich, więc typowo podaje się jeden lub dwa.
headers to obiekt string-to-string dla Twoich własnych nagłówków, na przykład {"X-Campaign": "spring-2026"}, z limitem 25 nagłówków o wartościach do 998 znaków. Trzy rodzaje nagłówków zwracają 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.
- List-Unsubscribe i List-Unsubscribe-Post w wysyłce marketing. Ustawiamy na nich zgodny nagłówek rezygnacji jednym kliknięciem samodzielnie. W wysyłce transactional zostawiamy Twoje dokładnie tak, jak je ustawisz.
- Dowolna wartość zawierająca znak powrotu karetki lub nowej linii.
Śledzenie
track_opens i track_clicks domyślnie mają wartość true. Ustaw dowolne na false, aby pominąć wstrzykiwanie piksela otwarcia lub przepisywanie linków w tej wysyłce. Śledzenie i metryki opisuje, co każde z nich zmienia w wiadomości.
Kategoria i pula IP
category klasyfikuje treść i ustawia politykę suppressions: marketing blokuje dostarczanie przy każdym powodzie suppression i każdej rezygnacji, a transactional dostarcza mimo suppression 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 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 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 |
| 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 | RFC 3339 timestamp | nie | Zaplanuj wysyłkę treści inline lub template; zobacz planowanie wysyłki |
Wysyłanie z szablonem
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. template.parameters to miejsce, w którym podajesz wartości własnych parametrów szablonu, według nazwy. Pomiń jeden, a wysyłka zostanie odrzucona z błędem 422 wskazującym jego nazwę. Wszystko inne w wysyłce działa tak samo jak inline, w tym odbiorcy, tags, metadata, śledzenie i załączniki. Co jest specyficzne dla wysyłki z szablonem:
- Inline albo szablon, nigdy oba. Wysłanie template razem z subject, html lub text jest odrzucane z błędem 422. API odrzuca również wartości zmiennych w polu parameters najwyższego poziomu; w wysyłce z szablonem należy je umieścić w template.parameters.
- bird to jedyna zarezerwowana nazwa. Ścieżka placeholdera zaczynająca się od bird. wskazuje nasze własne dane, takie jak link rezygnacji czy rekord kontaktu odbiorcy, więc klucz template.parameters nie może nosić nazwy bird. 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ę. Utrwalamy opublikowaną wersję, wybrany język i wartości parametrów w momencie przyjęcia. Jeśli usuniesz szablon przed zaplanowanym czasem wysyłki, wiadomość zostanie odrzucona z błędem generation_failure.
- 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łędem 422.
- language wybiera 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 ustawienie on_missing_language decyduje, czy zamiast tego zostanie wysłane najbliższe dopasowanie, czy wysyłka zostanie odrzucona. Szablon z ustawieniem language_source_required odrzuca 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ć.
Tagi a metadane
Oba dołączają Twoje własne dane do wysyłki i różnią się sposobem późniejszego odpytywania:
- tags to 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 jak campaign, experiment_variant czy source.
- metadata to 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.
Każde zdarzenie webhooka zawiera oba 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 jako wymiar analityki.
Przykład kodu
{
"tags": [{ "name": "campaign", "value": "onboarding" }],
"metadata": { "user_id": "usr_12345", "order_id": "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:
Przykład kodu
{
"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ść html ani text. Gdy przechowywanie treści jest włączone dla obszaru roboczego, zapisane treści pozostają dostępne przez 30 dni z GET /v1/email/messages/{message_id}/content.
- Odczyt może chwilowo nie nadążać za wysyłką. 404 na endpointach odczytu tuż po 202 oznacza, ż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.
Rozliczenia
Wysyłki e-mail są mierzone per odbiorca względem miesięcznego limitu Twojego planu, więc wiadomość do trzech odbiorców zużywa trzy wysyłki. Rozliczenia i zużycie opisują model mierzenia i odczyt bieżącego zużycia.
Następne kroki
- Szablony e-mail: twórz i publikuj szablony, które tu wysyłasz
- Kategorie: jak marketing i transactional zmieniają zachowanie suppressions
- Suppressions: 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
Kontynuuj z dokumentacją, przewodnikami i przykładami dotyczącymi tego tematu. Zasoby są w języku angielskim.
Obejrzyj przewodnikOrder confirmation emailsPoznaj możliwościOrder confirmation emailsPodążaj ścieżką naukiBuild your first integration
Wypróbuj ćwiczenie i uzyskaj brief wdrożeniowy