信頼性の高いメールAPI連携で重要なのは、何かがうまくいかないときの挙動です。正常系は1回のHTTPリクエストにすぎません。難しいのは、メッセージが受け付けられたか分からないタイムアウト、バッチの途中でワーカーがクラッシュするケース、そして短い障害を大量のリクエストに変えてしまうリトライループです。本ガイドでは、どのプロバイダーのエンドポイントを呼び出す場合でも、すべてのメールを「ちょうど1回」、最悪でも予測可能な形で1回だけ送るためのパターンを紹介します。
ここで紹介するパターンは特定の製品に依存しないもので、MigoSMTPを含むあらゆるHTTPメールAPIで使えます。冪等性ヘッダー、バッチ用エンドポイント、レート制限など個別の機能が関わる場合は、推測せず、MigoSMTPのアカウントまたはAPIドキュメントで正確な内容をご確認ください。まだプロトコルを選んでいる段階であれば、先にSMTPとAPI:送信方法の選び方をお読みください。
メール送信が他のAPI呼び出しと違う形で失敗する理由
ほとんどのAPI呼び出しは、繰り返しても害がありません。メールは違います。ユーザー作成を2回呼び出せば通常は競合エラーになりますが、領収書の送信を2回呼び出せば、領収書がそのまま2通送られます。お客様には2通届き、サポートには苦情が入り、パスワードリセットが二重に送られると、本人が読んでいる最初のコードが無効になってしまうこともあります。
根本的な問題は、結果が不明になることです。クライアントがリクエストを送り、サーバーが受け付けたのに、ネットワーク切断やタイムアウトで応答が失われます。送信側から見ると、リクエストが届かなかった場合と区別がつきません。リトライすれば重複するかもしれず、リトライしなければメッセージが失われるかもしれません。以下のパターンはすべて、この曖昧さを解消するためのものです。
| 障害 | 分かること | 単純に処理した場合のリスク |
|---|---|---|
| 接続拒否またはDNS障害 | リクエストはAPIに届いていない | リトライしても安全。リスクはリトライの集中だけ |
| リクエスト送信後の読み取りタイムアウト | 受付の有無は不明 | 重複排除キーなしのリトライは二重送信の恐れ |
| 429 Too Many Requests | ペース制御のためAPIが呼び出しを拒否した | 即時リトライは負荷をさらに悪化させる |
| APIまたはゲートウェイからの5xx | 通常は未受付だが、保証はない | 場合によってはタイムアウトと同じ曖昧さが残る |
| 400または422の検証エラー | リクエスト自体が誤っている | 延々とリトライすると容量を浪費し、バグが隠れる |
| API呼び出し後のワーカークラッシュ | 受付済みだが、記録されていない | ジョブが再配信され、メールが再度送られる |
冪等性キーと二重送信の防止
冪等な操作とは、何回実行しても結果が同じになる操作です。多くのHTTP APIでは、通常はヘッダーで冪等性キーを渡すことができ、同じキーで繰り返されたリクエストに対しては、新しいリソースを作らず最初の結果が返されます。IETFでは、標準のIdempotency-Keyヘッダーフィールドのドラフトが議論されており、この考え方が広く使われていることが分かります。MigoSMTPがこのようなヘッダーに対応しているか、またキーがどのくらいの期間保持されるかは、MigoSMTPのAPIドキュメントでご確認ください。ここでは対応しているものとは仮定しません。
朗報として、サーバー側の対応を待つ必要はありません。重複排除テーブルを使えば、同じ保証を自社側で構築できます。要点は、ネットワークに触れる前に、アプリケーションがメールの同一性を決めておくことです。
決定的な重複排除キーを選ぶ
良いキーは、送信時に生成するのではなく、業務イベントから導き出します。order-1042-shippedやuser-77-password-reset-2026-10-15T09:14のような形式です。ワーカー内で生成するランダムなUUIDは役に立ちません。ジョブが再配信されるたびに新しいUUIDができてしまうからです。同じ注文の領収書と発送通知が衝突しないようにメッセージ種別を含め、日次ダイジェストのように繰り返しの送信が正当な場合に限って、時間区分やカウンターを含めてください。
ユニーク制約で強制する
「検索してから挿入する」といったアプリケーション側のチェックは、競合状態を招きます。キーにユニークインデックスを設定し、勝敗の判定はデータベースに任せましょう。最初に書き込んだ側が送信レコードを作成します。その後の試行は、制約エラーになるか、既存の行を見つけてその状態を読み取ります。
CREATE TABLE email_sends (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
dedupe_key VARCHAR(190) NOT NULL,
to_address VARCHAR(320) NOT NULL,
template VARCHAR(80) NOT NULL,
payload_json JSON NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
attempts INT NOT NULL DEFAULT 0,
next_try_at DATETIME NULL,
message_id VARCHAR(255) NULL,
last_error TEXT NULL,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
UNIQUE KEY uq_dedupe (dedupe_key)
);
ここで使うステータスは、pending、sending、accepted、failed、unknownの5つです。acceptedは、APIが成功を返し、メッセージIDを保存した状態を指します。unknownは、呼び出しは行ったものの結果が確かでない状態で、リトライの節で扱います。APIが冪等性キーに対応している場合は、同じ重複排除キーをそちらにも渡し、両方の層で整合させてください。サーバー側の対応は代替ではなく、二重の安全策として扱います。
アウトボックスパターン:コミットしたものだけを送る
典型的なバグは次のようなものです。コードが注文を保存し、メールAPIを呼び出して、応答を返す。APIの呼び出しが失敗すると、注文はあるのにメールがありません。逆にAPIを先に呼び出してデータベースのコミットが失敗すると、存在しない注文の確認メールがお客様に届きます。2つのシステムを1つのトランザクションで更新することはできないため、問題の捉え方を変えます。
アウトボックスパターンでは、メールのリクエストを、業務上の変更と同じトランザクションで書き込まれる単なる1行として扱います。両方がコミットされるか、どちらもされないかのいずれかです。その後、別のプロセスがアウトボックスの行をAPIに届けます。配信はリトライ可能なバックグラウンドジョブとなり、送信すべき内容についてはデータベースが唯一の正となります。
- トランザクション:注文とemail_sendsの行を、ステータスpendingで一緒に挿入します。
- コミット:コミットされて初めて、送信プロセスから行が見えるようになります。
- 確保:ワーカーが送信時刻の来たpendingの行を選択してsendingにマークします。行ロックを使い、2つのワーカーが同じ行を取らないようにします。
- API呼び出し:リクエストを送り、結果とメッセージIDを行に記録します。
- 照合:定期ジョブが、sendingやunknownのまま止まっている行を探して解決します。
ワーカーへの供給は、数秒ごとにテーブルをポーリングする方法と、コミット後にキューへ発行する方法があります。ポーリングのほうが単純で、構成要素も少なく済みます。ボリュームが大きい場合はキューが有効ですが、真実の記録としてはテーブルを維持してください。キューはメッセージを再配信したり失ったりすることがあり、テーブルがあれば「このメールはどうなったのか」を確認できるからです。
アプリ内のキュー処理:ワーカー、同時実行、順序
強い理由がない限り、Webリクエストの中でメールAPIを呼び出さないでください。サードパーティの応答が遅いと、そのまま決済も遅くなり、サインアップの急増がそのまま接続数の急増につながります。アウトボックスの行をコミットした時点でユーザーに応答を返し、配信はバックグラウンドのワーカーに任せましょう。
- 同時実行数に上限を設ける。ワーカー数を固定し、それぞれに小さなHTTPコネクションプールを持たせると、毎秒のリクエスト数の上限が予測可能になります。並列数に制限がないと、アプリが意図せずAPIの制限に達してしまいます。
- 緊急度ごとにキューを分ける。パスワードリセットやワンタイムコードを、ニュースレターの後ろに並ばせてはいけません。トランザクションメールとマーケティングメールの配信ストリームの分離で説明しているように、キューや優先度を分けてください。
- ジョブを冪等にする。どのジョブも2回配信され得ると考えます。ジョブの最初の処理は、行を読み込み、ステータスがすでにacceptedなら終了することです。
- 順序に頼らない。リトライや並列ワーカーによって、メッセージの順序は入れ替わります。確認メールをウェルカムメールより先に送るなど、順序が重要な場合は、キューの順序ではなくデータ上の依存関係として表現してください。
- 古くなったメールを失効させる。20分後に届くログインコードは、届かないより悪い結果になります。時間的に重要な行には有効期限を設定し、送信せずに期限切れとしてマークします。
ペイロードは小さく保ってください。テンプレート名と変数、または最終的にレンダリングした内容を保存し、大きな添付ファイルをキューのメッセージに入れないでください。保存済みファイルはIDで参照し、サイズ制限とMIMEのガイドで説明している制限に合わせます。
タイムアウトとリトライの分類
すべてのHTTP呼び出しに、明示的なタイムアウトが必要です。タイムアウトのないクライアントは、人間が気にする時間よりはるかに長く待ち、その間ずっとワーカーの枠を占有します。接続タイムアウトは短く(多くは1~2秒)、合計タイムアウトは通常の応答には十分長く、かつ素早く失敗できる程度に短く設定します。値は、ブログ記事にある数字ではなく、実測したレイテンシーとMigoSMTPのAPIドキュメントの指針をもとに決めてください。
呼び出しが終わったら、結果を3つの区分に振り分けます。この分類こそが連携の中核です。
| 結果 | 例 | 対応 |
|---|---|---|
| 受付済み | メッセージID付きの2xx | IDを保存し、acceptedにして終了 |
| 確実に拒否、リトライしない | 400、401、403、404、422、その他リクエスト内容に起因する4xx | failedにしてエラー本文を記録し、繰り返す場合はアラート。コード、認証情報、データを修正する |
| 後でリトライ、同じ重複排除キー | 429、500、502、503、504、接続エラー、DNSエラー | バックオフしてリトライ。Retry-Afterに従う |
| 結果不明 | 読み取りタイムアウト、送信後の接続リセット | 同じ重複排除キーでのみリトライするか、先に照合する |
これらのコードに関するHTTPの仕様(Retry-Afterヘッダーを含む)はRFC 9110で、429はRFC 6585で定義されています。SMTPとの違いに注意してください。SMTPでは4xxの応答は一時的、5xxは恒久的なものを表します。これはSMTPバウンスコードのガイドで説明しています。HTTP APIでは役割が異なり、5xxはサービス側に問題があること、4xxはリクエスト側に問題があることを意味します。2つのモデルを混同しないでください。HTTPの400は、一時的なメール障害ではありません。
ジッター付きバックオフ
リトライの間隔は指数的に延ばし、ランダム性も加えてください。同時に失敗した1,000個のワーカーが、同時に戻ってこないようにするためです。一般的な方法は、基本の待ち時間を試行ごとに2倍にし、最大値で頭打ちにして、ランダムな割合を適用するものです。応答にRetry-Afterがあれば、それを最小値として使います。最大試行回数または最大経過時間を設定し、それを超えたら行をfailedに移してアラートを出します。戦略の詳細は、レート制限、リトライ、バックオフのガイドで解説しています。
import random, time
RETRY_STATUS = {429, 500, 502, 503, 504}
def next_delay(attempt, retry_after=None, base=2.0, cap=300.0):
delay = min(cap, base * (2 ** attempt))
delay = random.uniform(delay / 2, delay)
if retry_after is not None:
delay = max(delay, float(retry_after))
return delay
def deliver(row, client):
try:
resp = client.post("/send", json=row.payload,
headers={"X-Dedupe-Key": row.dedupe_key},
timeout=(2, 15))
except (ConnectionError, TimeoutError):
return ("retry", next_delay(row.attempts))
if 200 <= resp.status_code < 300:
return ("accepted", resp.json().get("id"))
if resp.status_code in RETRY_STATUS:
ra = resp.headers.get("Retry-After")
return ("retry", next_delay(row.attempts, ra))
return ("failed", resp.text[:500])
この例のヘッダー名は、説明のために作ったものです。MigoSMTPのAPIドキュメントで指定されているものを使うか、そのようなヘッダーがない場合は自社のテーブルだけに頼ってください。
レスポンスの処理とメッセージIDの保存
成功レスポンスは終わりではなく、追跡の始まりです。レスポンスのメッセージIDは、行をacceptedにするのと同じ処理の中で、すぐに送信レコードに保存してください。そうすれば、配信・バウンス・苦情のWebhookイベントをこのIDに結び付けられます。この後半部分の信頼性については、メールWebhookを確実に処理する方法で扱っています。
- APIのレスポンス本文は、短期間保管しておきます。デバッグの際に非常に役立ちます。
- 作成、最初の試行、受付、最後のイベントのタイムスタンプを記録します。その間隔を見れば、遅延の発生箇所が分かります。
- 「受付済み」の意味を理解してください。メールAPIの2xx応答は、通常、サービスが配信のためにメッセージをキューに入れたことを意味し、受信者のサーバーが受け付けたことを意味しません。最終的な配信結果は、後からイベントで通知されます。
- エラーメッセージを解析してロジックに使わないでください。ステータスコードとドキュメント化されたエラーコードを使い、自由記述のテキストは人間向けの情報として扱います。
- メッセージIDの列にインデックスを付けます。サポートが、ログにあるIDから顧客レコードまで1回のクエリーでたどれるようになります。
ログでは、シークレットや個人データをマスクしてください。重複排除キー、送信ID、ステータス、メッセージIDは記録しても、APIキーやメッセージ本文の全文は記録しません。キーの保管とローテーションは、APIキーのセキュリティで説明している方法に従ってください。
バッチ処理と一括送信
多数の宛先への送信は、領収書1通の送信とは別のワークロードです。スループット、メモリ、他のトラフィックへの公平性を管理することになります。1回のリクエストで多数のメッセージを受け付けるバッチ用エンドポイントを提供するメールAPIもあれば、1リクエストにつき1メッセージで、多数の呼び出しを並行させる前提のものもあります。どちらかを前提に設計する前に、利用可能な機能とリクエストあたりの宛先数の上限を、MigoSMTPのアカウントまたはAPIドキュメントで確認してください。
- 宛先をチャンク化する。宛先は、巨大なリストとして一度にメモリに載せるのではなく、データベースからページ単位で読み込みます。宛先ごとに送信行を作成し、ワーカーに処理させます。
- 宛先ごとにパーソナライズする。ToやCcに多数の宛先を入れるのは避けます。一人ひとりに、専用の配信停止リンクと追跡データを付けた個別のメッセージを送ってください。
- 送信のペースを制御する。大規模なキャンペーンは、一度にすべて放出せず、ある程度の時間枠に分散させます。ペース制御は、ウォームアップのガイドで説明しているように、特に新しいドメインでレピュテーションを守る効果もあります。
- キャンペーンを再開可能にする。各宛先がcampaign-318-user-9001のような一意の重複排除キーを持つ行を持つので、送信をどの時点で止めて再開しても、二重送信は起きません。
- 部分的な失敗に対応する。バッチ呼び出しで一部のメッセージだけ成功し、他が失敗することがあり得る場合は、メッセージごとの結果を保存します。ドキュメントにそう書かれていない限り、1つのステータスコードだけでバッチ全体を受付済みや失敗にしないでください。
リストの基本を省略しないでください。APIでの一括送信でも、同意、サプレッション、配信停止に関するルールは変わりません。送信行を作成する前に、サプレッションリストのガイドの方法で、抑止対象のアドレスを除外してください。
実例:1回の障害と2つの設計
ここでは、切りのよい数字を使った架空の例を示します。あるオンラインショップは、注文のたびに注文確認メールを送っています。ある混雑した午後、1時間に6,000件の注文が入り、メールAPIで4分間の応答遅延と断続的な503エラーが発生しました。
| ステップ | 単純な設計 | 重複排除キー付きのアウトボックス |
|---|---|---|
| 注文の作成 | リクエストがAPIを直接呼び出す | リクエストが注文と送信行を書き込み、すぐに応答する |
| APIが4分間遅い | 決済が長いタイムアウトで待たされ、エラーを見て再度「支払う」をクリックする顧客もいる | 決済は影響を受けず、行はpendingで待機する |
| 150件の呼び出しでタイムアウト | コードが即座にリトライ。そのうち多くのメッセージはすでに受け付けられていた | 行はunknownにマークされ、後で同じ重複排除キーでリトライされる |
| 結果 | 150件の注文でそれぞれ約2通の確認メール、さらにダブルクリックによる重複注文 | 約150通が数分遅れたが、重複はゼロ |
| サポートへの影響 | どのメールが正しいのかを尋ねる問い合わせが数十件 | なし。キュー滞留のアラートが発報し、解消した |
1時間に6,000件の注文なら、平均は毎分100件です。そのため4分間の遅延で、約400行の滞留が生じます。APIの復旧後にワーカーが毎分300通を送れるなら、新規の到着(毎分100件)を超える余力は毎分200通なので、滞留は約2分(400行÷毎分200通)で解消します。この計算は、自社のシステムでもぜひ行ってください。ワーカーの余力が復旧の速さを決め、同時実行数の上限が、復旧がアカウントの制限内に収まるかどうかを決めます。
レート制限への配慮
レート制限には、毎秒のリクエスト数、1日あたりのメッセージ数、同時接続数、リクエストあたりの宛先数などの形があります。どれがアカウントに適用され、正確な値がいくつかは、MigoSMTPのアカウントまたはAPIドキュメントに記載されています。推測しないでください。数値はコード内の定数ではなく、設定値としてクライアントを作りましょう。
- 各ワーカーでトークンバケットまたは固定の間隔を使い、すべてのワーカーを通じた全体の上限も設けます。
- 429を受けたら、そのワーカーからの送信をRetry-Afterの期間、指定がなければバックオフの時間だけ止めます。同じ呼び出しをすぐに再試行しないでください。
- 「遅い成功」に注意します。レイテンシーの上昇傾向は、エラーより先に現れることが多く、同時実行数を減らす合図になります。
- サーキットブレーカーを使います。失敗が続いたら、しばらく呼び出しを止め、1件のリクエストで様子を見てから、全速力に戻します。
- 受信側の制限も念頭に置いてください。プロバイダーによる受け付けは、最初の一歩にすぎません。メールボックスプロバイダーは独自の制限を設けており、バルク送信者の要件のガイドで取り上げています。
APIが停止したときの段階的な機能縮退
外部サービスは、いつか必ず利用できなくなります。障害の最中に場当たり的に対応しなくて済むよう、製品としてどう振る舞うかを事前に決めておきましょう。
| メールの種類 | 障害時に推奨される動作 |
|---|---|
| パスワードリセット、ワンタイムコード | 短い有効期限でキューに入れる。メッセージが遅れる可能性があることを画面に表示し、クールダウン後の再送を提案する |
| 注文確認・支払い確認 | キューに入れて遅れて配信する。注文ページにも確認内容を表示する |
| アカウント通知 | キューに入れて遅れて配信する。複数をまとめて1通にすることも検討する |
| ニュースレター、キャンペーン | キャンペーンを一時停止して後で再開する。遅れを取り戻そうと急がない |
| 社内アラート | チャットや2つ目のプロバイダーなど、別の経路にフォールバックする |
アラートは、エラー数だけでなく、キューの深さと最も古いpending行の経過時間にも設定してください。エラーがなくてもキューが増え続けているなら、お客様は待たされています。メモリやストレージが際限なく増えないようにキューの上限を設け、上限に達した場合に何を最初に破棄するかも決めておきます。フォールバック用に2つ目のプロバイダーを設計する場合は、移行ガイドで説明しているように、2つ目の経路のウォームアップと認証に手間がかかることを忘れないでください。
連携のテスト
200応答だけを確認するテストでは、守れません。重要なのは失敗時の挙動なので、失敗を簡単に再現できるようにしましょう。応答を遅らせる、Retry-After付きの429を返す、503を返す、リクエストを読んだ後に接続を切る、不正な本文を返す、といった指示ができるモックサーバーまたはサンドボックスに対して、クライアントを実行します。内容の確認にはメールキャッチャーを使い、ステージング環境のガイドに従ってください。
- 重複ジョブのテスト:同じジョブを2回エンキューし、モックAPIに届くリクエストが1件だけであることを検証します。
- クラッシュのテスト:API呼び出しの後、データベース更新の前にワーカーを強制終了し、リコンサイラーが二重送信なしに行を解決することを検証します。
- タイムアウトのテスト:モックにリクエストを受け付けさせた後で応答を止め、行がちょうど1回だけacceptedになって終わることを確認します。
- ポイズンメッセージのテスト:422を返すペイロードを送り、1回の試行でエラーを保存したままfailedに移ることを確認します。
- 負荷テスト:ステージング環境で現実的なバーストを実行し、同時実行数の上限とバックオフが設計どおりに動くことを確認します。
公開前チェックリスト
- すべてのメールが決定的な重複排除キーを持ち、ユニークインデックスで強制されている。
- 送信行が、業務イベントと同じトランザクションで書き込まれている(アウトボックス)。
- 冪等性ヘッダーへの対応状況をMigoSMTPのAPIドキュメントで確認したうえで、対応の有無にかかわらず自前のテーブルを使っている。
- APIを呼び出すのはWebリクエストではなくワーカーで、同時実行数に上限がある。
- すべての呼び出しに接続タイムアウトと合計タイムアウトが設定されている。
- 応答が分類されている:受付済み、リトライ、失敗、不明。その他の4xxは無条件にリトライしない。
- バックオフにジッターと上限と最大試行回数があり、Retry-Afterに従っている。
- メッセージIDが送信行に保存され、インデックスが付いている。
- 一括送信は、チャンク化、ペース制御、再開可能で、行の作成前にサプレッションで除外している。
- レート制限は、アカウントまたはAPIドキュメントから取得した設定値になっている。
- 最も古いpending行の経過時間、失敗数、429の発生率にアラートがある。
- 重複、クラッシュ、タイムアウト、429の失敗テストがステージング環境で通っている。
送信設計について、第三者の目で確認してほしいですか?現在アプリケーションがどのようにメールを送っているか、リトライや重複について不安な点をお知らせください。安全な連携の計画をお手伝いします。
参考資料と出典
よくある質問
メールAPIにおける冪等性キーとは何ですか?
リクエストに付与する一意の値で、サーバー側が同じリクエストの繰り返しを認識し、処理を二重に実行しないようにするためのものです。メールの場合は、タイムアウト後にリトライしても同じメッセージが2通届かないようにする役割を果たします。MigoSMTPが冪等性ヘッダーに対応しているかどうか、またその方法については、MigoSMTPのAPIドキュメントでご確認ください。いずれの場合も、自前の重複排除テーブルで同等の保護を実現できます。
APIが冪等性に対応している場合でも、自前の重複排除テーブルは必要ですか?
はい、必要だと考えています。サーバー側の冪等性は、通常、限られた期間と、APIに到達したリクエストだけが対象です。自前のテーブルがあれば、上流で発生する重複ジョブ、Webhookの重複トリガー、ダブルクリックも防げます。さらに、送信しようとした内容の監査記録も残ります。
タイムアウト後にリトライしても安全ですか?
重複排除の仕組みがある場合に限ります。タイムアウトは、APIがメッセージを受け付けたかどうかが分からない状態です。冪等性キーや自社の参照IDによる照会がなければ、リトライによって同じメールが2回送られる可能性があります。これらがあれば、リトライは安全です。
どのHTTPステータスコードをリトライすべきですか?
ネットワーク障害、タイムアウト、429 Too Many Requests、5xxサーバーエラーはリトライ対象です。その際は、ジッター付きの指数バックオフを使い、Retry-Afterヘッダーがあれば従ってください。400、401、403、422などその他の4xxエラーは、何かを変更しない限り同じ結果になるため、リトライしないでください。
アウトボックスパターンとは何ですか?
業務上の変更と同じデータベーストランザクション内で、メール送信リクエストをテーブルに書き込み、別のプロセスが未送信の行を読み取ってAPIを呼び出す方式です。データベースのコミットは成功したのにメール送信が失敗する、あるいはその逆、という食い違いをなくせます。
メールはWebリクエスト内で送るべきですか、それともキュー経由ですか?
ほぼすべてのケースでキュー経由をお勧めします。キューを使うと、ユーザーに見える応答時間を守り、安全にリトライでき、送信レートも制御できます。例外は、ごく単純な社内ツールで、送信の失敗をそのまま操作した本人に表示すれば済む場合です。
APIで大規模なキャンペーンを送るにはどうすればよいですか?
宛先をバッチに分割し、同時実行数に上限を設け、ドキュメントに記載された制限とRetry-Afterを守り、送信を時間的に分散させます。正確な制限値やバッチ用エンドポイントの有無は、MigoSMTPのアカウントまたはAPIドキュメントでご確認ください。新しいドメインやIPの場合は、ウォームアップ計画にも従ってください。
APIが返すメッセージIDを保存するのはなぜですか?
送信記録と、その後の配信・バウンス・苦情といったWebhookイベントを結び付ける結合キーになるためです。これがないと、「このお客様は領収書を受け取ったのか」といったサポートへの問い合わせが推測頼みになってしまいます。
実際にメールを送らずにメール連携をテストするにはどうすればよいですか?
遅い応答、429、5xxを任意に返せるモックサーバーまたはサンドボックスに対してAPIクライアントを実行し、内容の確認にはメールキャッチャーを使います。HTTP呼び出しだけでなく、自社の送信テーブルの状態を検証してください。安全な構成例は、ステージング環境のガイドで紹介しています。