Przejdź do dokumentacji
SDK

Wyślij e-mail

`emails.send`: jedna wiadomość, teraz albo później.

emails.send

send-email.ts
const email = await openemail.emails.send({  from: { email: '[email protected]', name: 'Acme Billing' },  to: ['[email protected]', 'Grace <[email protected]>'],  cc: '[email protected]',  bcc: [{ email: '[email protected]' }],  replyTo: '[email protected]',  subject: 'Your September invoice',  html: '<p>Invoice attached.</p>',  text: 'Invoice attached.',  headers: { 'X-Campaign': 'invoices' },  attachments: [{ filename: 'invoice.pdf', content: pdfBytes }],  threadId: 'thread_…',  scheduledAt: 'PT1H',  tags: { order: '4021' },  tracking: { opens: true, clicks: true },})

to, cc i bcc przyjmują jednego odbiorcę albo wielu, a pojedynczy jest opakowywany za Ciebie. Każdy może być samym adresem, Name <addr@host> albo { email, name }.

Parametry

fromRecipientInputwymagane
Nadawca. Sam adres, `Name <addr@host>` albo obiekt. Musi to być adres, z którego ten klucz może wysyłać. Nie ma nadawcy zastępczego, bo zastępczym nadawcą byłby domyślny adres obszaru roboczego, który zmienia się, gdy adresy pojawiają się i znikają.
toRecipientInput | RecipientInput[]wymagane
Jeden odbiorca albo wielu; pojedynczy jest opakowywany za Ciebie. Najwyżej 50 łącznie w to, cc i bcc.
ccRecipientInput | RecipientInput[]
Liczy się do limitu 50 odbiorców.
bccRecipientInput | RecipientInput[]
Nigdy nienazywane w bajtach, które dostaje ktokolwiek inny, bo na każdego odbiorcę transmitowana jest osobna koperta.
replyToRecipientInput
Pojedynczy adres, wysyłany jako nagłówek Reply-To.
subjectstring
Najwyżej 998 znaków, czyli limit linii z RFC 5322. Domyślnie pusty.
htmlstring
Wymagane jest jedno z html, text, draftId albo template. HTML jest tym, co widzą odbiorcy, gdy podano i html, i text.
textstring
Część w czystym tekście.
template{ id, version?, props?, slots? }
Wyrenderuj zapisany szablon po stronie serwera. `version` przypina wersję; pomiń je, żeby użyć tej, która jest opublikowana w chwili przyjęcia żądania. Nieznana albo brakująca właściwość daje 422, a nie puste miejsce w wiadomości.
draftIdstring
Wyślij zapisaną wersję roboczą pod tą kopertą.
headersRecord<string, string>
`X-*`, `List-*`, Reply-To, Precedence, Auto-Submitted, Importance, Priority i Feedback-Id. Wszystko, co transport ustawia sam, jest odrzucane, a nie po cichu pomijane.
attachmentsAttachmentInput[]
`{ filename, content, contentType? }` albo `{ fileId }` nazywające plik już obecny w przestrzeni roboczej. Przekaż bajty jako content, a zostaną zakodowane w base64 za Ciebie. 20 plików, przy czym pliki inline łącznie do 5 MB po zdekodowaniu. Plik przechowywany może być większy i podróżuje jako link do pobrania.
attachmentDeliveryAttachmentDeliveryMode
`mime`, `link` albo `auto`. `auto` niesie pliki jako linki do pobrania, gdy przekroczą 2 MB w domenie z aktywną domeną plików, a w przeciwnym razie wewnątrz wiadomości. Pominięte — obowiązuje ustawienie skrzynki, a ono domyślnie ma `auto`.
threadIdstring
Odpowiedź w istniejącym wątku. Transport zapisuje In-Reply-To i References.
scheduledAtDate | string
Date, instant ISO-8601 albo czas trwania w rodzaju `PT1H`. Do roku naprzód, nigdy w przeszłość. Nie można łączyć z cancellableForSeconds.
cancellableForSecondsnumber
Od 0 do 900. Okno cofnięcia przy wysyłce natychmiastowej: mechanizm cofania z kompozytora, wystawiony zamiast zaszyty na sztywno.
tagsRecord<string, string>
Do 10 etykiet, odsyłanych z powrotem i filtrowalnych. Nigdy nieinterpretowane.
signatureboolean
Czy ta wiadomość niesie podpis adresu, z którego jest wysyłana, czyli własny podpis tego adresu albo ten ustawiony dla Wszystkich adresów. Domyślnie true, bo podpis należy do adresu, a nie do tego, który klient wysłał wiadomość. Ustaw `false` dla poczty, którą program wysyła w czyimś imieniu, na przykład potwierdzenia, resetu hasła albo zestawienia — żadne z nich nie chce mieć pod sobą podpisu człowieka.
tracking{ opens?, clicks? }
Czy dodać piksel otwarcia i przepisać linki dla tej wiadomości. Włączone, chyba że właściciel przestrzeni roboczej wyłączył śledzenie dla adresu, z którego wiadomość jest wysyłana, albo dla Wszystkich adresów; każde z tych pól podane tutaj rozstrzyga tę jedną wiadomość niezależnie od ustawienia adresu.
translate{ to, from?, subject?, includeOriginal? }
Wyślij ją w języku odbiorcy. `to` przyjmuje kod, angielską nazwę albo własną nazwę języka; `subject` i `includeOriginal` domyślnie są true. Rozwiązywane, gdy żądanie zostaje przyjęte, więc zaplanowana wiadomość niesie słowa, które zatwierdzono. Odrzucane obok `draftId`.

Odpowiedź

idstring
Identyfikator wysyłki, `msg_…`. Używaj go w `get`, `cancel`, `reschedule` i `getTracking`.
statusEmailStatus
queued, scheduled, sending, sent, partial, cancelled albo failed. Czytaj to, a nie fakt, że obietnica się rozwiązała. `partial` to osobny stan: część odbiorców ma wiadomość i nie da się jej cofnąć, więc ponawianie jest błędem, a raportowanie porażki kłamstwem.
mode'live' | 'test'
Jakiego rodzaju klucz ją wysłał. Wysyłka testowa jest zapisywana i nigdy nie jest transmitowana.
fromstring
Adres faktycznie autoryzowany i umieszczony na łączu, nie zawsze ten, o który proszono.
subjectstring | null
Tak jak wysłano.
messageIdstring | null
Message-ID z RFC 5322. Null, dopóki nie powstanie MIME. Usługa wysyłkowa przepisuje ten nagłówek na wyjściu, więc żaden bounce ani raport doręczenia nie niesie tej wartości. `id` jest tym, na czym wraca zdarzenie.
threadIdstring | null
Wątek, w którym wylądowała.
transportstring | null
Jak wiadomość wyszła. Null do czasu wysłania.
attemptsnumber
Ile razy próbowano wysłać.
lastErrorstring | null
Dlaczego ostatnia próba się nie powiodła, dosłownie.
scheduledAtstring | null
Instant ISO, w którym ma wyjść.
cancellableUntilstring | null
Dopóki teraz jest przed tą chwilą, anulowanie działa.
sentAtstring | null
Instant ISO, w którym wyszła.
tagsRecord<string, string>
To, co wysłałeś, odesłane z powrotem.
sourceEmailSource
composer, api, mcp, ai albo queue: która powierzchnia poprosiła. `api` to ten klient.
createdAtstring
Instant ISO, w którym zapisano rekord.
replayedboolean
True, gdy `Idempotency-Key` pasował do wysyłki, która już istniała. Nic nowego nie wysłano, a to jest oryginalna wiadomość.
translationEmailTranslationResource | undefined
Obecne tylko na wiadomości, która została przetłumaczona, i tylko tam, gdzie niesione jest całe zapisane żądanie: w tej odpowiedzi i w `get`. `{ language, languageName, detectedSourceLanguage, subject, includeOriginal }`, same kody, a nie wiersze języków. Wiersz listy nigdy tego nie ma, więc brak w nim nie mówi nic w żadną stronę.

W języku odbiorcy

translate zapisuje wiadomość w czyimś innym języku, zanim ona wyjdzie. Treść, a jeśli tego nie wyłączysz, to i temat, są tłumaczone w chwili przyjęcia żądania przez API, a to, co z tego wyszło, jest tym, co idzie dalej: tłumaczenie, którego nie udało się wytworzyć, odrzuca wysyłkę, zamiast nadawać ją w języku, w którym ją napisałeś.

translate.ts
const email = await openemail.emails.send({  from: '[email protected]',  to: '[email protected]',  subject: 'Your September invoice',  html: '<p>Invoice attached. Payment is due on the 14th.</p>',  translate: { to: 'de' },}) console.log(email.translation)// { language: 'de', languageName: 'German', detectedSourceLanguage: 'en', subject: true, includeOriginal: true }

Nikt tego nie przeczytał, zanim wyszło. emails.translate to ta sama podróż, zatrzymana krok wcześniej. Pokaż to człowiekowi, pozwól mu zmienić, a potem wyślij to, co zatwierdził, w ogóle bez translate w wywołaniu. Ponowne przekazanie przetłumaczyłoby tekst drugi raz i wyrzuciło jego poprawki.

preview-translation.ts
const preview = await openemail.emails.translate({  subject: 'Your September invoice',  html: '<p>Invoice attached. Payment is due on the 14th.</p>',  to: 'de',}) console.log(preview.language.native, preview.detectedSourceLanguage) const approved = await showToSomebody(preview) await openemail.emails.send({  from: '[email protected]',  to: '[email protected]',  subject: approved.subject,  html: approved.html,})
render-picker.ts
import { LANGUAGES, isRtlLanguage, languageByCode, openemail, resolveLanguage } from '@openemail/sdk' LANGUAGES.length // 200 const current = await openemail.languages.list() resolveLanguage('Deutsch')?.code // 'de'resolveLanguage('zh-TW')?.code // 'zh-Hant'languageByCode('DE')?.native // 'Deutsch'isRtlLanguage('ar') // true

Tabela jest dołączona do paczki, w kolejności do wyboru, więc listę wyboru można wypełnić jeszcze przed pierwszym żądaniem. languages.list() rozwiązuje się tymi samymi wierszami z łącza, jako zwykła tablica, dla kogoś, kto woli te bieżące niż te, z którymi wyszła ta wersja. resolveLanguage przyjmuje kod, nazwę angielską, endonim albo alias (zh-TW jest aliasem kodu, którego już nie ma na liście), languageByCode dopasowuje dokładny kod bez względu na wielkość liter, a szesnaście wierszy zapisuje się od prawej do lewej. Przeszukuj native, label i code razem, pokazuj najpierw native i zapisuj kod.

emails.translate nie jest ponawiane automatycznie. Kosztuje wywołania modelu i niczego nie zapisuje, więc nie ma czego czynić idempotentnym, a ponowienie po nieodebranej odpowiedzi kupiłoby tylko tę samą odpowiedź dwa razy.

  • Język, który nie rozwiązuje się do niczego, to validation_error na translate.to, zanim cokolwiek wyjdzie.
  • translation_too_long powyżej 30 000 znaków, translation_not_configured, gdy instalacja nie ma skonfigurowanego AI, translation_failed, gdy dostawca nie odpowiedział. Żaden z nich nie wysyła wiadomości nieprzetłumaczonej w ramach zapasowego rozwiązania.
  • Działa z template: tłumaczone jest WYRENDEROWANE wyjście, więc jedna zapisana treść obsługuje każdy język, w którym czytają Twoi klienci. Szablon renderujący cały dokument zachowuje swój doctype, bloki <style> i reguły @font-face: do modelu idzie tylko treść, a reszta jest z powrotem wokół niej odtwarzana. Jego <title> pozostaje nietknięty i tak niczego nie wyświetla.
  • Ponowienie nie kosztuje nic dodatkowo. Tłumaczenie nie jest częścią odcisku idempotencji (żądanie jest, wraz z translate), więc ponowienie nieodebranej wysyłki z tym samym Idempotency-Key odtwarza wiadomość, która już istnieje, zamiast tłumaczyć i wysyłać drugą.
  • Przetłumaczona wiadomość, która jest w kolejce albo zaplanowana, jest zamrożona na zmiany treści. emails.reschedule nadal ją przesuwa; zmiana tego, co mówi, oznacza anulowanie i wysłanie od nowa.

Załączniki

content jest na łączu w base64. Przekaż bajty, a zostaną zakodowane za Ciebie.

attachment.ts
attachments: [  { filename: 'invoice.pdf', content: pdfBytes, contentType: 'application/pdf' },]

toBase64 jest eksportowane, gdybyś potrzebował go gdzie indziej. Dzieli dane na kawałki, czego btoa(String.fromCharCode(...bytes)) nie robi. Tamto wywraca się na wszystkim powyżej mniej więcej 100 kB i wywraca się na prawdziwym pliku, a nie na tym, na którym testowałeś.