【Amazon SES】同一のメール配信リクエストに対するメールイベントの発生のしかたについて調べてみた

### はじめに
はじめまして、qiitaに初投稿します。
普段はネットプロテクションズのNP掛け払いでプロダクト作りをしています。

この度テックブログを書くことになり、このテーマを選ぶことにしました。
元々事業企画からシステムに異動したこともあり、生粋のエンジニアではないので、
純粋に技術に関する話というより、少し事業やプロダクト目線が多めの内容になっております。
システムは日々勉強中ですので、温かい目で見守っていただけると幸いです。

### 調べた経緯
弊社のNP掛け払いというサービスは、
法人間決済の請求業務をまるっと代行できるサービスです。
（[BtoB・企業間後払い決済/請求代行サービス「NP掛け払い」](https://np-kakebarai.com/)）
今回SESを利用したのは、PDF形式の請求書をメールで送付する部分になります。
（請求書送付方法は郵送など他手段もあります）
それまでメール配信は外部サービスを使っていたのですが、
内製で作り直す機会があったので、Amazon SESを作って再構築しました。

請求書を送付するという業務性質上、確実に届けることと、
請求書の到着、開封状況を把握することが重要になります。
Amazon SESでは送信リクエスト後にメールイベントが取得できるため、これを利用することにしました。
ただ、1リクエストに対してどのイベントが発生するのか、重複するのかに関して、
いくら調べても出てこなかったので自分で実際にリクエストを送りながら調べてみました。
備忘も兼ねてこちらに記載できればと思います。

##### 注意
+ サブスクリプションイベントは当時、利用予定がなかったので調べられておりません
+ 自分の手で調べたものなので、情報の正確性は当てにしないでください
心配なかたはご自身で調べていただけると幸いです
もし記載内容に間違いがあればコメントでご指摘いただけると嬉しいです
+ 調べたのは2021年の8月ごろですので、現在の仕様は変わっているかもしれません
+ イベントの中身はawsの公式ドキュメントをご参考ください
参考：[Amazon SES が Amazon SNS に発行するイベントデータのコンテンツ](https://docs.aws.amazon.com/ja_jp/ses/latest/dg/event-publishing-retrieving-sns-contents.html)


### 同一リクエストに対する、イベントの被り
![ddafc372d149fed7b2239400d2e0d5f8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1201349/e2834de8-5d4d-2dae-9dca-10e5f925d127.png)

はい、こんな感じでした。
これだけみても良くわからないと思うので、
イベントを取得できる時系列で少し補足してみます。
（rejectなど再現ができず少し怪しい部分は「?」をつけています）

1. メール送信リクエストをしたものはまず、rejectかsendのイベントが取得できます
（弊社ではrejectは実運用ではめったに発生しないので、99.99%sendです）
2. sendされたものは、renderingfailure,delivery,bounce,deliverydelayのいずれかのイベントが取得されます
3. deliveryされたものは、complaint,open,click,subscriptionなどが取得できます

なお、この中で一番厄介なのがdeliverydelayでして、
・deliverydelayのあとにdeliveryが取得できるケース
・deliverydelayのあとにsoft bounceが取得できるケース
がありえます。
この辺を気にしながら取得イベントの選定、実装する必要があります。
なお、SESで取得できるメールイベントは、SESのconfiguration setで設定できます。
&emsp;参考：[Amazon SES でのイベント送信先の作成](https://docs.aws.amazon.com/ja_jp/ses/latest/dg/event-destinations-manage.html)


### おすすめのイベント取得の組み合わせ
最後に、用途別おすすめのイベントの組み合わせを記載しておきます。

#### textメールしか送らない場合
+ reject
+ renderingfailure
+ delivery/bounce
+ complaint

reject
&emsp;SES内でウィルス検知されたケースのもので、滅多に（というか過去一度も）発生したことはありません。
&emsp;ただ、発生した場合は、社内にセキュリティリスクがあることが考えられますので、
&emsp;受け取ることが必要かなと考えます。

renderingfailure
&emsp;メールテンプレートとパラメータが噛み合わない場合などに発生します。
&emsp;当然メールも遅れません。
&emsp;これは結構トラップなのですが、sesにsdk経由でrequestするタイミングでは正常終了するんですね。
&emsp;なので、イベントとして受け取らないと、実は送れていないのに気づけないということが発生します。
&emsp;結構な罠です。

delivery/bounce
&emsp;請求書の送付という業務性質上、極力到着率を高める必要があります。
&emsp;ただメールという基盤は、到着するかどうかがいろんな要素に左右される不安定なものでもあります。
&emsp;誰もが使える一方で、悪用する人が多いので、受信側でしっかり制御をかけてるからですね。
&emsp;（到着率を高めるための工夫も結構あるのですが、記事が長くなるのでまたの機会に）
&emsp;なので、到着/未着のデータはしっかりとって、DBに刻むようにしています。
&emsp;この未着履歴をもとに、別の媒体（SMSやオートコールなど）で連絡を取るようにしています。


#### htmlメールを送る場合
+ reject
+ renderingfailure
+ delivery
+ bounce
+ complaint
+ open
+ click

open/click
&emsp;htmlメール限定でとれるイベントです。
&emsp;今どきはほとんどのサービスがhtmlメールを送ってると思いますが、
&emsp;メールを送った後に、受信側の動向が探れる唯一のものですので、
&emsp;ぜひ受け取りたいですね。
&emsp;ただ、htmlメールを開けないクライアントを利用されてる受信者の方もいらっしゃるので、
&emsp;open/clickだけですべてのお客さんをカバーできてるわけではありません。

### イベント受け取りのアーキテクチャ

最後に、簡単ではありますが、イベント受け取り周りのアーキテクチャを貼っておきます。

![f261cae8ca8237b918a7fecaf3a93fd1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1201349/bc7db0cb-a834-4700-7453-7e0c24eeafcf.png)

なおSESからのイベントの受け取りはSNSでFIFOが使えないので、スタンダードで対応してます。
配信結果テーブルへの登録時に冪等チェックをするようにしています。
冪等チェックでは、宛先メールアドレスと、
sesへの送信リクエスト時に送れるtagsを使ってます。
&emsp;参考：[Amazon SES イベント発行を使用して E メール送信をモニタリングする](https://docs.aws.amazon.com/ja_jp/ses/latest/dg/monitor-using-event-publishing.html)
tagsに差し込んだkvのセットと、宛先メアドをdynamoのパーティションキーとソートキーにして、
それで冪等チェックをするようにしています。

拙い内容でしたが、自分が実装するときはこの情報が調べてもヒットしなかったので、
ここに残しておきます。
どなたかの参考になれば幸いです。