Wyślij wiadomość
POST /emails: jedna wiadomość, teraz albo później.
Uruchamia prawdziwe wywołanie na twojej przestrzeni roboczej, twoim własnym kluczem.
Żądanie
from jest wymagane. W przeciwieństwie do edytora wiadomości nie ma nadawcy zastępczego, bo tym zastępczym nadawcą jest domyślny adres obszaru roboczego, który zmienia się niepostrzeżenie, gdy adresy pojawiają się i znikają.
| Pole | Wymagane | Uwagi |
|---|---|---|
| from | tak | Sam adres albo Name <addr>. Musi być adresem, z którego klucz może wysyłać. |
| to | tak | Łącznie do 50 odbiorców w to, cc i bcc. |
| cc, bcc | nie | Odbiorcy bcc nigdy nie są wymieniani w bajtach, które otrzymuje ktokolwiek inny. |
| subject | nie | Domyślnie pusty. |
| html, text | jedno z | Oba naraz są w porządku. To HTML widzą odbiorcy. |
| template | jedno z | { id, version?, props?, slots? }. Zapisana treść, po id albo po slugu. Odrzucane razem z html, text lub draftId. Zobacz Wysyłka z szablonem. |
| replyTo | nie | Pojedynczy adres. |
| headers | nie | X-*, List-*, Reply-To, Precedence, Auto-Submitted, Importance, Priority, Feedback-Id. |
| attachments | nie | { filename, content, contentType } w base64, łącznie 5 MB, albo { fileId } wskazujące plik już obecny w przestrzeni roboczej. 20 plików. |
| attachmentDelivery | nie | mime, link albo auto. auto linkuje pliki, gdy przekroczą 2 MB, na domenie z aktywną domeną plików. Domyślnie ustawienie skrzynki. |
| threadId | nie | Odpowiedź w istniejącym wątku. |
| draftId | nie | Wysyłka istniejącej wersji roboczej. |
| scheduledAt | nie | Moment ISO albo czas trwania. Zobacz Planowanie. |
| cancellableForSeconds | nie | Okno cofnięcia od 0 do 900 sekund przy wysyłce natychmiastowej. Odrzucane razem ze scheduledAt, którą i tak można anulować do chwili wysłania. Zobacz Planowanie. |
| signature | nie | false pomija podpis w tej wiadomości. W przeciwnym razie niesie ona podpis adresu, z którego jest wysyłana — własny podpis tego adresu albo ten ustawiony dla Wszystkich adresów. |
| tags | nie | Do 10 własnych etykiet. Zwracane z powrotem, nigdy interpretowane. |
| tracking | nie | { opens?, clicks? }. Każde z nich nadpisuje ustawienie dla tej wiadomości; pomiń pole, a ta połowa spadnie do ustawienia adresu, z którego wysyłasz, a dalej do Wszystkich adresów — i jest włączona, chyba że któreś z nich ją wyłączyło. |
| translate | nie | { to, from?, subject?, includeOriginal? }. Wysyła wiadomość w języku odbiorcy. Rozstrzygane w chwili przyjęcia żądania, odrzucane razem z draftId. |
Nieznane pola są odrzucane, a nie ignorowane, więc źle zapisana nazwa daje 422 teraz, zamiast niespodzianki później. Nagłówki, które podważyłyby autoryzację nadawcy (From, Sender, Bcc, Message-ID, Return-Path i inne), są odrzucane z reserved_header.
Odpowiedź
200, gdy wiadomość już poszła, 202, gdy coś jeszcze musi się z nią wydarzyć. Wywołujący, który rozgałęzia się na kodzie stanu, ma rację w obu przypadkach.
{ "object": "email", "id": "msg_c5f21cc6bfec4e848caf905b", "status": "sent", "mode": "live", "from": "[email protected]", "subject": "Your September invoice", "messageId": "<2598…@acme.com>", "transport": "ses", "sentAt": "2026-08-29T08:19:08.000Z", "source": "api", "replayed": false}id to trwały uchwyt, który zachowujesz, i ten, na który wraca zdarzenie dostarczenia, ponieważ webhook o odbiciu nazywa go emailId. messageId to Message-ID zgodny z RFC 5322 i jest null, dopóki nie powstanie MIME. Nie koreluj po nim: usługa wysyłkowa przepisuje ten nagłówek w drodze na zewnątrz, więc wartość podana tutaj nie pojawia się w żadnym raporcie o odbiciu ani dostarczeniu, a dopasowanie po niej nigdy nie zadziała.
W języku odbiorcy
translate zapisuje wiadomość w cudzym języku, zanim ona wyjdzie. Treść, a jeśli tego nie wyłączysz — także temat, są tłumaczone w momencie, w którym żądanie zostaje PRZYJĘTE. To ta sama reguła, którą stosuje template, i jest nośna z tych samych powodów: zaplanowana wiadomość niesie słowa, które zostały zatwierdzone, a nie to, co model wyprodukuje we wtorek, a tłumaczenie, którego nie udało się uzyskać, odrzuca wysyłkę, zanim powstanie jakikolwiek wiersz. Nic nie zostaje dostarczone w języku, którego nadawca nie wybrał.
translate
tostringwymagane- Język, w którym ma powstać tekst: kod BCP-47 (`de`), nazwa angielska („German”) albo własna nazwa języka („Deutsch”), od 2 do 60 znaków. Wszystkie trzy formy są normalizowane do kodu z tabeli, zanim wydarzy się cokolwiek innego, więc stanowią jedno żądanie — co ma znaczenie, bo odcisk Idempotency-Key liczony jest z przetworzonego żądania. Aliasy również się rozwiązują: `zh-TW` staje się `zh-Hant`. Taki, który nie rozwiązuje się do niczego, daje 422 na `translate.to`.
fromstring- To, w czym napisałeś tekst, w dowolnej z tych samych trzech form. Czysta optymalizacja. Pominięte — treść zostaje odczytana, a język ustalony, co kosztuje jedno krótkie wywołanie modelu. Warto je podać na ścieżce o dużym wolumenie i warto podać je wtedy, gdy treść to głównie nazwiska, liczby i odnośniki: wykrywanie raczej wstrzymuje się od odpowiedzi, niż zgaduje, a nieustalone źródło nie kosztuje cię nic poza nazwą języka w podpisie nad twoim oryginałem. To nie jest `from` z najwyższego poziomu, które jest adresem.
subjectboolean- Przetłumacz także temat wiadomości. Domyślnie true; false wysyła temat dokładnie tak, jak go napisałeś.
includeOriginalboolean- Umieść to, co faktycznie napisałeś, pod tłumaczeniem, za separatorem i z podpisem w języku odbiorcy. Domyślnie true i warto to zostawić włączone. To jedyna rzecz, która pozwala czytającemu sprawdzić zdanie brzmiące dziwnie, zamiast prosić go o zaufanie modelowi, którego wyniku żadne z was nie widzi.
curl -X POST "$OE/emails" -H "$AUTH" -H "Content-Type: application/json" \ -d '{ "from": "[email protected]", "to": ["[email protected]"], "subject": "Your September invoice", "html": "<p>Invoice attached. Payment is due on the 14th.</p>", "translate": { "to": "de" } }'{ "object": "email", "id": "msg_c5f21cc6bfec4e848caf905b", "status": "sent", "from": "[email protected]", "subject": "Ihre Rechnung für September", "translation": { "language": "de", "languageName": "German", "detectedSourceLanguage": "en", "subject": true, "includeOriginal": true }}translation jest dodatkowe i pojawia się tylko przy wiadomości, która została przetłumaczona: w tej odpowiedzi i w GET /emails/{id}, nigdy w wierszu listy, ponieważ lista nie pobiera zapisanego żądania, a jej milczenie w tym miejscu nie mówi nic w żadną stronę. Niesie kody, a nie całe wiersze języków: jest zapisem tego, co zrobiono, a endonim mieszka w GET /languages. subject w odpowiedzi jest tym przetłumaczonym, więc konsola nigdy nie wypisze wiadomości pod ciągiem, którego odbiorca nie widział.
- Działa z
templatei to jest przypadek użyteczny: tłumaczony jest WYRENDEROWANY wynik, więc jedna zapisana treść obsługuje każdy język, w którym czytają twoi klienci. Szablon renderujący cały dokument jest najpierw rozbierany: do modelu trafia tylko to, co znajduje się wewnątrz<body>, a doctype, bloki<style>i reguły@font-facesą z powrotem doklejane wokół odpowiedzi. Dlatego też limit 30 000 znaków mierzy prozę, a nie dokument: dwuwierszowa wiadomość owinięta w firmowy arkusz stylów jest dwuwierszową wiadomością. - Jedyną częścią szablonu pozostawioną bez tłumaczenia jest jego
<title>, którego nie wyświetla żaden klient poczty.<Preview>z react-email renderuje się do treści i jest tłumaczone razem z resztą. - Odrzucane razem z
draftId: 422 natranslate, z komunikatem „A draft is sent as it was written; translate a body or send a draft, not both”. Szkic napisał człowiek i jest wysyłany w takiej postaci, w jakiej go zostawił. - Celowo nie wchodzi w skład odcisku idempotencji. Haszowane jest żądanie, które wysłałeś, wraz z
translate; to, co wyprodukował model, już nie. Dlatego ponowienie nieodpowiedzianej wysyłki z tym samymIdempotency-Keyodtwarza oryginał. Wraca wiadomość, która już istnieje, bez drugiej wysyłki i bez drugiego tłumaczenia. Haszowanie samego brzmienia sprawiłoby, że uczciwe ponowienie za każdym razem dawałoby inny odcisk — a właśnie tak ta sama wiadomość wychodzi dwa razy. - Przetłumaczona wiadomość, która czeka w kolejce lub jest zaplanowana, jest zamrożona wobec zmian brzmienia. Przesuń ją albo anuluj; zmiana tego, co mówi, oznacza anulowanie i ponowną wysyłkę, przy kimś, kto potrafi przeczytać nowe słowa.
- Język pisany od prawej do lewej jest produkowany od prawej do lewej: tłumaczenie owinięte w
dir="rtl", twój oryginał poniżej zorientowany po swojemu. Atrybut przechodzi przez wychodzący sanitizer, który dopuszczadirdokładnie z tego powodu, więc wiadomość w transmisji niesie kierunek pokazany w podglądzie.
| Kod | Status | Kiedy |
|---|---|---|
| `invalid_parameter` | 422 | translate.to albo translate.from wskazuje język, którego nie potrafimy umiejscowić. Komunikat podaje, które trzy formy są akceptowane, i kieruje do GET /languages. |
| `unknown_language` | 422 | Ta sama awaria wychwycona o krok później, przez usługę, a nie przez schemat. Zabezpieczenie, na translate.to. |
| `translation_too_long` | 422 | Powyżej 30 000 znaków po którejkolwiek stronie wywołania modelu. Odmowa zamiast obcięcia: połowa przetłumaczonej wiadomości nie ma szwu, który pokazywałby, gdzie się urwała, a czytający działa na podstawie tej połowy, którą dostał. |
| `translation_not_configured` | 409 | Przestrzeń robocza nie ma klucza AI, a platformowe AI jest wyłączone. 409, a nie 503, ponieważ ponowienie zawiedzie identycznie. Nic nie zostało wysłane. Wyślij bez translate, jeśli chciałeś wysłać tekst tak, jak został napisany. |
| `translation_failed` | 503 | Dostawca nie odpowiedział albo odpowiedział czymś nieużywalnym. Nic nie zostało wysłane; wiadomość nigdy nie jest awaryjnie nadawana bez tłumaczenia. Ten błąd jest po naszej stronie i warto go ponowić. |
| `unknown_parameter` | 422 | Nierozpoznany klucz wewnątrz translate, które jest obiektem ścisłym, jak reszta żądania. |
Wysyłka z kodu nie ma nikogo, kto najpierw przeczyta tłumaczenie. POST /emails/translate to ta sama podróż w obie strony zatrzymana o krok wcześniej, po to, by pokazać człowiekowi, co za chwilę wyśle. Potem wyślij to, co zatwierdził, jako zwykłe html/subject, bez translate w żądaniu w ogóle.