メールを送信する
`emails->send`: 1 通のメッセージを、今またはあとで送ります。
emails->send
$email = $client->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' => new \SplFileInfo('invoice.pdf')]], 'threadId' => 'CAHk7pQ2x9LmZ4-mail.example.com', 'scheduledAt' => 'PT1H', 'tags' => ['order' => '4021'], 'tracking' => ['opens' => true, 'clicks' => true],]); echo $email['id'], ' ', $email['status'], PHP_EOL;to、cc、bcc は受信者 1 人、または受信者のリストを受け取り、1 人だけの場合は自動的にリストに包まれます。それぞれ素のアドレス、Name <addr@host>、または email と name を持つ配列のいずれかです。
メッセージは API のフィールド名をキーとする 1 つの配列です。そのため replyTo と scheduledAt は camelCase のままです。一方 idempotencyKey: と apiKey: は呼び出しの名前付き引数で、メッセージの一部にはなりません。前に作ったメッセージのフィールドを 1 つだけ変えるには、新しい配列に展開します。$client->emails->send([...$message, 'subject' => 'Re: your invoice']) は他のフィールドをすべて保ったまま件名を置き換えます。
パラメーター
fromstring or array必須- 送信者。素のアドレス、`Name <addr@host>`、または `email` と `name` を持つ配列です。このキーで送信できるアドレスでなければならず、そうでなければ呼び出しは 403 `from_address_forbidden` をスローします。代わりの送信者はないため、送信では常に送信元のアドレスを指定します。
tostring or array必須- 受信者 1 人、または受信者のリストで、1 人だけの場合は自動的に包まれます。`to`、`cc`、`bcc` を合わせて最大 50 人で、それを超えると 422 `too_many_recipients` になります。
ccstring or array- 50 人の受信者上限に数えられます。
bccstring or array- 受信者ごとに 1 つのエンベロープが送信されるため、他の誰かが受け取るバイト列にこのアドレスが現れることはありません。これも 50 人に数えられます。
replyTostring or array- 単一のアドレスで、Reply-To ヘッダーとして送られます。
subjectstring- 最大 998 文字で、RFC 5322 の行の上限です。既定値は空で、空の件名はテンプレートまたは下書きの件名にフォールバックします。
htmlstring- `html`、`text`、`draftId`、`template` のいずれか 1 つが必要です。`html` と `text` の両方が与えられたとき、受信者が見るのは HTML です。最大 1,000,000 文字。
textstring- プレーンテキストの部分。最大 1,000,000 文字。
templatearray- 保存済みのテンプレートをサーバー側でレンダリングします。`id`(id または slug を受け付けます)と、省略可能な `version`(int)、`props`、`slots` を持つ配列です。`version` はリビジョンを固定します。省略すると、リクエストが受け付けられた時点で公開されているものが使われます。不明な prop や欠けている prop は、メッセージ内の空欄ではなく 422 になります。
draftIdstring- 保存済みの下書きを、書かれたとおりにこのエンベロープで送信します。`template` や `translate` とは組み合わせられません。
headersarray- ヘッダー名から文字列の値への対応で、`X-*`、`List-*`、Reply-To、Precedence、Auto-Submitted、Importance、Priority、Feedback-ID に限られます。トランスポートが自分で設定するものは、黙って捨てられるのではなく 422 `reserved_header` で拒否されます。
attachmentsarray- リストで、各エントリは `filename`、`content`、省略可能な `contentType` を持つ配列、または `fileId` だけを持つ配列です。後者は `files->upload` で追加したものなど、すでにワークスペースにあるファイルを指します。`content` は base64 です。`fopen` のストリーム、`SplFileInfo`、PSR-7 ストリームは読み込んで自動的にエンコードされ、文字列はあらかじめ base64 になっている必要があります。ファイルは 20 個までで、インラインファイルはデコード後の合計で 5 MB が上限です。保存済みのファイルはそれより大きくてもよく、ダウンロードリンクとして送られます。
attachmentDeliverystring- `mime`、`link`、`auto` のいずれか。`auto` は、files ドメインが有効なドメインでファイルが 2 MB を超えるとダウンロードリンクとして送り、それ以外はメッセージ内に入れます。省略するとメールボックスの設定が適用され、その既定は `auto` です。
threadIdstring- 既存のスレッドへ返信します。トランスポートが In-Reply-To と References を書き込みます。
scheduledAtDateTimeInterface or string- UTC の ISO 8601 時刻として送られる `DateTimeInterface`、文字列の ISO 8601 時刻、または `PT1H` のような期間。1 年先まで指定でき、過去は指定できません。`cancellableForSeconds` とは組み合わせられません。`2027-01-01` のような時刻のない日付文字列は、その日の UTC 午前 0 時として読まれるため、時刻が重要なときは時刻まで含めて渡してください。
cancellableForSecondsint- 0 から 900。即時送信における取り消し猶予で、コンポーザーの取り消し機構をハードコードせずに公開したものです。
tagsarray- 最大 10 個のラベル。キーは英字、数字、`_`、`-` からなる 1〜64 文字、値は最大 256 文字の文字列です。読み取りのたびにそのまま返され、解釈されることはありません。
signaturebool- このメッセージに、送信元アドレスの署名を付けるかどうか。そのアドレス自身の署名、catch-all が受け取ったアドレスならその catch-all の署名、それもなければ OpenEmail のフッターが付きます(そのアドレスでフッターを無効にしていない場合)。省略すると、`html` のボディは書かれたとおり署名なしで送られ、`text` だけのボディには付きます。領収書、パスワードリセット、ダイジェストなど、プログラムが誰かに代わって送るメールには false を設定してください。どれも個人の署名を付けるべきものではありません。テンプレート送信と暗号化送信には決して付きません。
trackingarray- 省略可能な `opens` と `clicks`(それぞれ bool)を持つ配列で、このメッセージに開封ピクセルを追加しリンクを書き換えるかどうかを指定します。送信元アドレス(またはそれを受け取った catch-all)でトラッキングが有効になっていない限りオフで、ここで指定したキーは、アドレスの設定がどうであれ、このメッセージについての扱いを決めます。
translatearray- 受信者の言語で送信します。`to` と、省略可能な `from`、`subject`、`includeOriginal` を持つ配列です。`to` はコード、英語名、またはその言語自身での名前を受け付け、`subject` と `includeOriginal` はどちらも既定で true です。リクエストが受け付けられた時点で確定するため、スケジュールされたメッセージは承認された文面で送られます。`draftId` と一緒に指定すると拒否されます。
idempotencyKeystring- メッセージのフィールドではなく、呼び出しの名前付き引数です。この送信のための独自のキーで、英字、数字、`_`、`.`、`:`、`-` からなる 1〜255 文字。指定しない場合、クライアントは呼び出しごとにキーを生成するため、自身のリトライで二重送信することはありません。指定すると、別のプロセスで再実行された送信は繰り返されずにリプレイされます。
apiKeystring- これも名前付き引数です。複数のワークスペースに代わって送信するプロセスのために、クライアントのキーではなくこのキーで送信します。
レスポンス
API の camelCase の名前をキーとする配列なので、$email['status'] でステータスを読めます。
idstring- 送信 id で、`msg_` の後に 16 進数 24 文字が続きます。`get`、`cancel`、`reschedule`、`getTracking` に使います。
statusstring- queued、scheduled、sending、sent、partial、bounced、cancelled、failed のいずれか。呼び出しが戻ったという事実ではなく、これを読んでください。即時送信はリクエスト内で配送され、通常は `sent`、`partial`、`failed` で返り、保留された送信は `queued` または `scheduled` で返ります。`partial` はそれ自体が 1 つの状態です。一部の受信者にはすでに届いていて取り消せないため、リトライは誤りで、失敗と報告するのは事実に反します。
modestring- `live` または `test`:どちらの種類のキーで送信したか。テスト送信は記録されますが、実際には送信されません。`transport` が `test` で、ステータスは `sent` になるため、受信箱ではなくレスポンスに対してアサーションしてください。
fromstring- 実際に認可され通信に載ったアドレスで、求められたものとは限りません。
subjectstring or null- 送られたとおりです。
messageIdstring or null- RFC 5322 の Message-ID。MIME ができるまでは null です。送信サービスは送り出すときにこのヘッダーを書き換えるため、バウンスや配信レポートにこの値が含まれることはありません。イベントが返ってくるときの手がかりは `id` です。
threadIdstring or null- 着地したスレッドです。
transportstring or null- メッセージがどの経路で送られたか。配送されるまでは null。
attemptsint- 送出が何回試みられたか。
lastErrorstring or null- 直近の試行が失敗した理由を、そのまま記したものです。
scheduledAtstring or null- 送信予定の ISO 8601 時刻。
cancellableUntilstring or null- 現在時刻がこれより前である間は、`cancel` がまだ有効です。
sentAtstring or null- 送信された ISO 8601 時刻。
tagsarray- 送ったものが、そのまま返ります。
sourcestring- composer、api、mcp、ai、queue のいずれか。どの面が要求したかを示します。このクライアントは `api` です。
createdAtstring- レコードが書き込まれた ISO 8601 時刻。
replayedbool- Idempotency-Key が既存の送信と一致したときに true。新たには何も送信されておらず、これは元のメッセージの現在の状態です。
translationarray- 翻訳されたメッセージにだけ存在し、保存されたリクエスト全体を含む場所、つまりこのレスポンスと `get` にだけ現れます。`language`、`languageName`、`detectedSourceLanguage`、`subject`、`includeOriginal` を持ち、言語の行全体ではなくコードで表します。一覧の行には決して含まれないため、そこに無いことは何も意味しません。
受信者の言語で送る
translate は、メッセージが出ていく前にそれを別の人の言語で書き直します。本文と、無効にしない限り件名が、API がリクエストを受け付けた時点で翻訳され、その出力がそのまま送られます。翻訳を生成できなかった場合は、あなたが書いた言語のまま投函するのではなく送信を拒否します。
$email = $client->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'],]); print_r($email['translation'] ?? []);このとき $email['translation'] は、language が de、languageName が German、detectedSourceLanguage が en で、subject と includeOriginal はどちらも true です。
それは送られる前に誰も読んでいません。emails->translate は同じ往復を 1 段階手前で止めたものです。結果を人に見せて修正してもらい、承認されたものを、呼び出しに translate を一切付けずに送信してください。もう一度渡すと 2 回目の翻訳が行われ、修正が捨てられてしまいます。
$preview = $client->emails->translate([ 'subject' => 'Your September invoice', 'html' => '<p>Invoice attached. Payment is due on the 14th.</p>', 'to' => 'de',]); echo $preview['language']['native'], PHP_EOL, $preview['subject'], PHP_EOL, $preview['html'], PHP_EOL;echo 'Send it as it is? [y/N] '; $answer = fgets(STDIN); if ($answer !== false && strtolower(trim($answer)) === 'y') { $client->emails->send([ 'from' => '[email protected]', 'to' => '[email protected]', 'subject' => $preview['subject'], 'html' => $preview['html'], ]);}use OpenEmail\Constants\Languages;use OpenEmail\OpenEmail; echo count(Languages::ALL), PHP_EOL; $current = $client->languages->list();echo count($current), PHP_EOL; echo OpenEmail::resolveLanguage('Deutsch')['code'] ?? 'none', PHP_EOL;echo OpenEmail::resolveLanguage('zh-TW')['code'] ?? 'none', PHP_EOL;echo OpenEmail::languageByCode('DE')['native'] ?? 'none', PHP_EOL;var_dump(OpenEmail::isRtlLanguage('ar'));これらの行は、このバージョンに同梱されている行数の 200、API が現在持つ行数、続いて de、zh-Hant、Deutsch、bool(true) を出力します。この表は OpenEmail\Constants\Languages::ALL として、ピッカーの表示順で同梱されています。code、label、native、flag、rtl を持つ配列のリストなので、最初のリクエストの前にピッカーを埋められます。languages->list は同じ行を通信で取得し、通常のリストとして返します。このバージョンに同梱された行ではなく現在の行を使いたい呼び出し元向けです。OpenEmail::resolveLanguage() はコード、英語名、その言語自身での名前、または別名(zh-TW はもう一覧にないコードの別名です)を受け取り、一致するものがなければ null を返します。OpenEmail::languageByCode() は大文字小文字を問わずコードの完全一致で照合し、OpenEmail::isRtlLanguage() は言語が右から左に書かれるかどうかを返します。16 の行がそうした言語です。native、label、code をまとめて検索し、native を先に表示し、コードを保存してください。
emails->translate は自動的に再試行されません。モデル呼び出しを消費し、何も書き込まないので、冪等にすべきものがなく、応答のなかったリクエストの再試行は同じ答えを 2 度買うだけです。
- API が照合できない言語は、何かが送信される前に
translate.toに対するvalidation_errorになります。 - 30,000 文字を超えると
translation_too_long、インストールに AI が設定されていなければtranslation_not_configured、ワークスペースが当日の AI 操作を使い切っていれば 429ai_quota_exceeded(UTC の午前 0 時にリセットされ、リトライはされません)、プロバイダーが応答しなければtranslation_failedになります。いずれの場合も、代わりに未翻訳のメッセージが送られることはありません。 templateと併用できます。翻訳されるのはレンダリング後の出力なので、保存された 1 つの本文が、顧客が読むすべての言語に対応できます。文書全体をレンダリングするテンプレートは、doctype、<style>ブロック、@font-face規則を保ちます。モデルへ渡されるのは body だけで、残りは後から周りに戻されます。<title>はそのままで、どのみち何も表示しません。- 再試行に追加費用はかかりません。翻訳は冪等性のフィンガープリントの一部ではなく(リクエストは
translateを含めて一部です)、応答のなかった送信を同じIdempotency-Keyで再試行すると、翻訳して 2 通目を送るのではなく、すでに存在するメッセージを再生します。 - queued または scheduled の翻訳済みメッセージは、承認された文面を保持します。
emails->rescheduleで時刻を動かすことはできますが、emails->updateは新しい文面を 409translation_lockedで拒否するため、内容を変えるにはキャンセルして送り直すことになります。
添付ファイル
content は通信上では base64 です。クライアントが読めるものを渡せば、バイト列は自動的にエンコードされます。fopen のストリームリソース、SplFileInfo、または PSR-7 のストリームかアップロードされたファイルを渡せます。文字列はそのまま送られるため、あらかじめ base64 になっている必要があります。メモリ上のバイト列は OpenEmail::toBase64() で base64 にできます。
use OpenEmail\OpenEmail; $attachments = [ ['filename' => 'invoice.pdf', 'content' => OpenEmail::toBase64(file_get_contents('invoice.pdf')), 'contentType' => 'application/pdf'], ['filename' => 'report.csv', 'content' => new \SplFileInfo('report.csv')], ['filename' => 'contacts.csv', 'content' => fopen('contacts.csv', 'rb')], ['fileId' => 'file_6bb640f5b99e47deb758f1f5'],]; $client->emails->send([ 'from' => '[email protected]', 'to' => '[email protected]', 'subject' => 'Your documents', 'text' => 'All three are attached.', 'attachments' => $attachments,]);base64 ではない文字列の content は、何かが送信される前に OpenEmail\Exception\InvalidArgumentException をスローします。たまたま base64 として読める生のバイト列は、代わりに文字化けした状態で送られてしまうため、ファイルのバイト列をそのまま渡さないでください。OpenEmail::toBase64() で包むか、ファイルそのものを渡してください。
同じエンコードが他の場所で必要なら OpenEmail::toBase64() があります。バイト列の文字列、ストリームリソース、SplFileInfo、PSR-7 ストリームを受け取り、改行のない base64 を返します。