Azure にデプロイした Web アプリケーションから、SendGrid を使ってメール送信しているのですが、docomo などのキャリアメールが割とバウンスしやすいです。バウンスした原因である受信拒否を解除しても、バウンスリストからメールアドレスを削除しないと、送信したメールが届きません。
sendgridjp.zendesk.com
SendGrid ポータルで、バウンスリストからメールアドレスを削除する運用が面倒になってきたこともあり、C# のプログラムから API を使って削除する方法を調べたのでまとめておきます。
SendGrid Bounces API
SendGrid には、バウンスリストを管理できる Web API が2つあります。
従来から Web API はありましたが、Web API v3 にも Bounces API が追加されました。提供されている機能が若干異なるのですが、バウンスリストから複数メールアドレスを一括で削除できる Web API v3 を使います。また、API キーによる認証がサポートされているので、セキュリティ的にもこちらのほうがお勧めです。
C# で Bounces API を呼び出す
Web API v3 の共通仕様、Delete bounces API の仕様を参考にしながら、シンプルに HttpClient で、Bounces API を呼び出しています。
internal static class SendGridApiHelper { private const string APIHOST = "https://api.sendgrid.com/v3/"; private const string BOUNCESAPI = APIHOST + "suppression/bounces"; private static readonly DateTime UNIX_EPOCH = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); public static async Task DeleteBouncesAsync(List<string> emails) { var targets = emails.Select((value, index) => new { value, index }).ToLookup(x => x.index / 500, x => x.value); foreach (var target in targets) { await DeleteBouncesAsync(target); } } private static async Task DeleteBouncesAsync(IGrouping<int, string> emails) { using (var client = new HttpClient()) { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "<SendGrid API Key>"); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var request = new HttpRequestMessage(HttpMethod.Delete, BOUNCESAPI); request.Content = new StringContent(JsonConvert.SerializeObject(new { emails = emails.ToList() })); request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); var response = await client.SendAsync(request); IEnumerable<string> values; if (response.Headers.TryGetValues("X-RateLimit-Limit", out values)) { var limit = values.First(); Trace.TraceInformation("Limit : " + limit); } if (response.Headers.TryGetValues("X-RateLimit-Remaining", out values)) { var remaining = values.First(); Trace.TraceInformation("Remaining : " + remaining); } if (response.Headers.TryGetValues("X-RateLimit-Reset", out values)) { var reset = UNIX_EPOCH.AddSeconds(Convert.ToInt64(values.First())); Trace.TraceInformation("Reset : " + reset.ToLocalTime().ToString()); } if (response.IsSuccessStatusCode == false) { var responseContent = await response.Content.ReadAsStringAsync(); if (String.IsNullOrEmpty(responseContent) == false) { Trace.TraceError("errors : " + responseContent); } } response.EnsureSuccessStatusCode(); } } }
ポイントは、3つあります。
- HttpClient の DeleteAsync() メソッドに Request Body を渡すことができないので、SendAsync() メソッドを使っています。DELETE の HttpRequestMessage を作って、Content にメールアドレスのリストを JSON でセットしています。Authorization ヘッダには、SendGrid ポータルで作成した API Key を渡します。
- メールアドレスを一括で削除できるのは 500 件までとなっているので、分割して API を呼び出しています。Linq の Enumerable.ToLookup() メソッドを使うと一発で分割することできて、いい感じでした。そもそも、こんなに多くのメールアドレスを一括で削除しないという話もありますが、他でも使えそうなロジックだと思います。
- Web API v3 には一定時間内に呼び出せる回数に制限があるため、レスポンスヘッダから情報を取得しています。回数上限に達した場合は、HTTP ステータスコードの 429(too many requests)が返されます。
まとめ
Bounces API を使えば、運用側の管理コストを減らしたり、ユーザーにバウンスを解除する機能を提供したりできます。しかし、必要以上にバウンスリストからメールアドレスを削除してしまうと、レピュテーションの低下に繋がりますので、利便性とリスクのトレードオフを十分に考慮したうえで使うようにしましょう。