ドキュメント本文へスキップ
API

メールを送信する

POST /emails: 1 通のメッセージを、今すぐまたは後で。

POSTapi.openemail.uk/emails

実際の呼び出しを、ご自身のキーで自分のワークスペースに対して実行します。

リクエスト

fromは必須です。コンポーザーとは違って代替の差出人がないのは、その代替がワークスペースの既定アドレスであり、アドレスが増減するたびに気づかないうちに変わってしまうからです。

フィールド必須備考
fromはい素のアドレス、または Name <addr>。キーが送信元にできるものである必要があります。
toはいto、cc、bcc を合わせて最大 50 名の受信者。
cc, bccいいえbcc の受信者が、他の人が受け取るバイト列に記載されることはありません。
subjectいいえ既定は空です。
html, textいずれか両方でも構いません。受信者が見るのは HTML です。
templateいずれか{ id, version?, props?, slots? }。保存済みの本文を id または slug で指定します。html、text、draftId と併用すると拒否されます。「テンプレートで送信する」を参照してください。
replyToいいえ単一のアドレスです。
headersいいえX-*List-*、Reply-To、Precedence、Auto-Submitted、Importance、Priority、Feedback-Id。
attachmentsいいえbase64 の { filename, content, contentType }(合計 5 MB まで)、またはワークスペースにすでにあるファイルを指す { fileId }。20 ファイルまで。
attachmentDeliveryいいえmimelinkauto のいずれか。auto は、有効なファイルドメインを持つドメインにおいて、ファイルが 2 MB を超えるとリンクにします。既定はメールボックスの設定です。
threadIdいいえ既存のスレッドに返信します。
draftIdいいえ既存の下書きを送信します。
scheduledAtいいえISO の時刻または期間。「予約」を参照してください。
cancellableForSecondsいいえ即時送信に対する 0 〜 900 秒の取り消し時間。scheduledAt と併用すると拒否されます。予約送信は送信されるまでキャンセル可能なままです。「予約」を参照してください。
signatureいいえfalse にすると、このメッセージには署名が付きません。それ以外の場合は送信元アドレスの署名が付きます。これはそのアドレス自身の署名か、なければ「すべてのアドレス」に設定された署名です。
tagsいいえ自分用のラベルを最大 10 個。そのまま返されるだけで、解釈されることはありません。
trackingいいえ{ opens?, clicks? }。どちらもこのメッセージに限って設定を上書きします。フィールドを省略すると、その半分は送信元アドレスの設定、なければ「すべてのアドレス」の設定にフォールバックし、そのいずれかがオフにしていない限りオンです。
translateいいえ{ to, from?, subject?, includeOriginal? }。受信者の言語で送信します。リクエストが受理された時点で解決され、draftId と併用すると拒否されます。

未知のフィールドは無視されず拒否されるため、名前のつづり間違いは後で驚くことにならず、その場で 422 になります。送信者認証を無効化しうるヘッダー(From、Sender、Bcc、Message-ID、Return-Path など)は reserved_header で拒否されます。

レスポンス

メッセージがすでに送信済みなら 200、まだ何かが起きる必要があるなら 202 です。ステータスコードで分岐する呼び出し側は、どちらについても正しく判断できます。

200 OK
{  "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 は保持すべき永続的なハンドルであり、配信イベントが返ってくるときの識別子でもあります。バウンスの webhook はこれを emailId として示します。messageId は RFC 5322 の Message-ID で、MIME が存在するまでは null です。これで突き合わせないでください。送信サービスが送出時にそのヘッダーを書き換えるため、ここにある値はどのバウンスレポートにも配信レポートにも現れず、これによる照合は決して成立しません。

受信者の言語で送る

translate は、送信前にメッセージを相手の言語で書き直します。本文と、オフにしない限り件名は、リクエストが受理された時点で翻訳されます。これは template が従うのと同じ規則で、理由も同じく重要です。予約されたメッセージは、火曜日にモデルが出力したものではなく、承認された文言を運びます。そして翻訳を生成できなかった場合は、行が作られる前に送信そのものが拒否されます。送信者が選ばなかった言語で配信されることはありません。

translate

tostring必須
書き込む言語です。BCP-47 コード(`de`)、英語名("German")、またはその言語自身の名称("Deutsch")で、2 〜 60 文字です。3 つとも、他の処理に先立ってテーブル上のコードへ正規化されるため、同一のリクエストとして扱われます。これは Idempotency-Key のフィンガープリントがパース後のリクエストに対して取られるため重要です。別名も解決され、`zh-TW` は `zh-Hant` になります。何にも解決されないものは `translate.to` に対する 422 です。
fromstring
元の言語を、同じ 3 つの形式のいずれかで指定します。純粋な最適化です。省略すると本文を読んで言語を判定し、短いモデル呼び出しが 1 回発生します。大量送信のパスでは指定する価値があり、本文がほとんど名前・数字・リンクである場合にも指定する価値があります。検出は推測せず判断を保留するため、元言語が不明でも失うのは、原文の上のキャプションに示される言語名だけです。トップレベルの `from` とは別物で、あちらはアドレスです。
subjectboolean
件名も翻訳します。既定は true で、false にすると件名は書いたとおりに送信されます。
includeOriginalboolean
実際に書いた内容を、区切り線の下に、受信者の言語のキャプション付きで翻訳の下に付けます。既定は true で、オンのままにしておく価値があります。これは、不自然に感じた一文を読み手が自分で確かめられるようにする唯一の手段です。そうでなければ、互いに出力を見られないモデルを信じろと求めることになります。
curl
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" }  }'
200 OK
{  "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 は追加情報であり、翻訳されたメッセージにのみ現れます。すなわちこのレスポンスと GET /emails/{id} にだけ現れ、一覧の行には決して現れません。一覧は保存されたリクエストを取得しないため、そこに無いことは何の意味も持ちません。言語の行全体ではなくコードを保持します。これは何が行われたかの記録であり、その言語自身の名称は GET /languages にあります。レスポンスの subject は翻訳後のものです。これにより、受信者が一度も目にしていない文字列でコンソールがメッセージを一覧することはありません。

  • template と併用できますし、それが有用なケースです。翻訳されるのはレンダリング後の出力なので、保存された 1 つの本文が、顧客が読むあらゆる言語に対応できます。文書全体をレンダリングするテンプレートはまず分解され、モデルに渡るのは <body> の中身だけで、doctype、<style> ブロック、@font-face ルールは回答の周りに戻されます。30,000 文字の上限が文書ではなく文章を測るのもこのためです。ブランドのスタイルシートに包まれた 2 行のメッセージは、やはり 2 行のメッセージです。
  • テンプレートで翻訳されない唯一の部分は <title> で、これを表示するメールクライアントはありません。react-email の <Preview> は本文にレンダリングされるため、他と一緒に翻訳されます。
  • draftId との併用は拒否されます。translate に対する 422 で、「下書きは書かれたとおりに送信されます。本文を翻訳するか下書きを送るかのどちらかにしてください」と返ります。下書きは人が書いたものであり、その人が残したとおりに送られます。
  • 意図的に冪等性のフィンガープリントには含まれません。ハッシュされるのは送ったリクエスト(translate を含む)であり、モデルが生成したものは含まれません。そのため、応答のなかった送信を同じ Idempotency-Key で再試行すると、元の結果が再生されます。すでに存在するメッセージが返り、2 度目の送信も 2 度目の翻訳も起きません。代わりに文言をハッシュしていたら、誠実な再試行のたびに異なるフィンガープリントになり、同じメッセージが 2 回送られることになります。
  • キューに入っている、または予約されている翻訳済みメッセージは、文言の変更に対して凍結されます。時刻変更やキャンセルはできますが、内容を変えるにはキャンセルして送り直すことになります。それも、新しい文言を読める人の前で行ってください。
  • 右から左に書く言語が対象の場合、出力も右から左になります。翻訳は dir="rtl" で包まれ、その下の原文は独自の向きになります。この属性は送信時のサニタイザーを通過します。サニタイザーがまさにこの理由で dir を許可しているため、送出されるメッセージはプレビューで見たとおりの方向を保ちます。
コードステータス発生条件
`invalid_parameter`422translate.to または translate.from が、特定できる言語を指していません。メッセージには受け付けられる 3 つの形式が示され、GET /languages が案内されます。
`unknown_language`422同じ失敗を、スキーマではなくサービス側で 1 段階あとに捕捉したものです。translate.to に対する保険です。
`translation_too_long`422モデル呼び出しの入力側または出力側が 30,000 文字を超えています。切り詰めではなく拒否です。途中で切れた翻訳文には、どこで止まったかを示す継ぎ目がなく、読んだ人は渡された半分をもとに行動してしまいます。
`translation_not_configured`409ワークスペースに AI キーがなく、プラットフォーム AI もオフです。再試行しても同じように失敗するため、503 ではなく 409 です。何も送信されていません。書いたとおりに送るつもりだったなら、translate なしで送信してください。
`translation_failed`503プロバイダーが応答しなかったか、使える内容を返しませんでした。何も送信されていません。フォールバックとして未翻訳のまま送られることは決してありません。これは当方側の問題であり、再試行する価値があります。
`unknown_parameter`422translate の中に認識できないキーがあります。リクエストの他の部分と同様、これは厳格なオブジェクトです。

コードからの送信では、翻訳を先に読む人がいません。POST /emails/translate は同じ往復を 1 段階手前で止めたもので、これから送る内容を人に見せるためのものです。その後、承認された内容を、リクエストに translate をまったく付けない通常の html / subject として送ってください。