📮

SendGrid Webhookを用いて送信失敗メールをキャッチする

2024/03/13に公開

はじめに

SendGrid APIを用いて送信したメールのうち、送信失敗したメールをキャッチしたかったので、方法を模索し実装しました。
実用性のあるメソッドだと思うので、記事として残そうと思います。

SendGridとは

サーバーを構築せずにAPIベースでメール送信をできるプラットフォームです。
導入はとても簡単です。詳しくは下記ページを参照。

https://sendgrid.kke.co.jp/

Webhookとは

外部アプリケーション連携手段の一つです。
APIと異なり、こちらから通信を行うのではなく、外部アプリケーション側から情報を送ってもらいます。
API通信の場合はこちらから都度通信を行わないと情報を取得できません。したがって、外部アプリケーション側でトリガーを持っており、リアルタイムで情報を取得したいときにWebhookが有用です。
下記記事の説明がわかりやすかったです。

https://qiita.com/soarflat/items/ed970f6dc59b2ab76169

SendGridでのメール送信

SendGridでメール送信APIを叩いて、メール送信機能を実行しています。

data = JSON.stringify({
    "personalizations": [
        {
            "to": [{"email": address}],
            "subject" : emailTitle
        }
    ],
    "from": {
        "email": fromAddress,
        "name": fromAddressName
    },
    "content": [
        {
            "type": "text/html",
            "value": value
        }
    ]
});
    
    
const config = {
    method: 'post',
    url: 'https://api.sendgrid.com/v3/mail/send',
    headers: { 
      'Authorization': `Bearer ${process.env.SENDGRID_AUTH_KEY}`, 
      'Content-Type': 'application/json'
    },
    data : data
};

await axios(config);

メールを送信したことはAPI responsestatus:202で判別できます。しかし、そのメールがきちんと届いたかどうかはAPI responseではキャッチできません。

メール送信状況はSendGridのActivityページで確認できますが、一定期間しか記録が残らず、また通知を受け取ることはできません。
そこで、SendGrid Webhookを構築してみました。

実装内容

SendGrid webhookを利用して、送信したメールのその後の状況をキャッチしました。
下記ページの通りに実行します。

https://sendgrid.kke.co.jp/docs/Tutorials/C_Manage_Events/using_event_webhook.html

SendGridのSettingsページ→Mail Settings/Webhook Settingsより新しいEventhookを作成します。
POST先URLとPOST対象イベントを選択します。
URLはCloud Functionsで新しいAPI関数のURLを作成することとしました。
今回は送信失敗メールをキャッチしたいため、対象イベントはDroppedBoucedを選択します。

作成すると、対象イベントが発生した場合に設定したエンドポイントURLにリクエストが飛んできます。

method: POST
headers: { host, user-agent, content-length, content-type, x-twilio-email-event-webhook-signature, x-twilio-email-event-webhook-timestamp, x-cloud-trace-context, traceparent, x-forwarded-for, x-forwarded-proto, forwarded, accept-encoding }
body: {[{email, event, reason, sg_event_id, sg_message_id, smtp-id, timestamp}]}

今回は飛んできたリクエストを元に、Slack APIを用いてチャンネルに投稿するシステムを実装してみました。

const data = JSON.stringify({
    "channel": channelName,
    "text": text
});

const config = {
method: 'post',
url: 'https://slack.com/api/chat.postMessage',
headers: { 
    'Authorization': `Bearer ${process.env.SLACK_BOT_OAUTH_TOKEN}`, 
    'Content-Type': 'application/json'
},
data : data
};

await axios(config);

デジタル署名の実装

SendGrid WebhookではWebhook用のURLに飛んできたAPIの送信元がSendGridであることを証明できるように、デジタル署名を利用できます。
デジタル署名とは公開鍵と秘密鍵を用いた暗号化技術を応用し、送信元が本人であることを証明する技術です。
デジタル署名についての説明とイメージについては、下記ページがわかりやすいです。

https://zerokara.dds.co.jp/signature/digitalsignature/

今回の場合は

暗号化のアルゴリズムは楕円曲線DSAというものを利用しています。

実装自体は簡単です。(参考ページ

import { EventWebhook } from "@sendgrid/eventwebhook";

const signature = req.headers["x-twilio-email-event-webhook-signature"] as string;
const timestamp = req.headers["x-twilio-email-event-webhook-timestamp"] as string;
const publicKey = process.env.SENDGRID_WEBHOOK_SIGNATURE; // 事前にSendGrid上で入手
const payload = req.rawBody;

const verify = new EventWebhook();
const ecdsaPublicKey = verify.convertPublicKeyToECDSA(publicKey);

const isVerified: boolean = verify.verifySignature(
  ecdsaPublicKey,
  payload,
  signature,
  timestamp
);

// => isVerified値のboolで判断する

また、他にもSendGridではOAuthを用いたVerificationも可能です!

まとめ

SendGrid Webhookを利用することでSendGridでのメール送信失敗をキャッチしてSlack通知を送ることができました!

Discussion