TL;DR: It’s very easy to do a coding error which leads to embarrassing duplicate customer emails. You can avoid this by making your emails idempotent but only few transactional email platforms support this feature.
My embarrassing moment with duplicate emails 😅
In one of my earlier workplaces we needed to send delayed emails for small part of our customers about the delivery for their order. We were already using Django and Celery workers for some background tasks but we had never yet used the delayed messages with
Task.apply_async()
. We tested this locally and everything seemed to work just fine so we shipped the feature for our customers.Few days later few of our customers reached back to us to inform that the feature is working well. It was actually working 32x more than expected and they had received our email 32 times 🤦♂️.
We debugged this with Celery and realised that the magic number 32 was also the amount of our celery workers in our cluster so all of the workers sent this same email. We didn’t immediately figure it out so we resorted to using PostgreSQL advisory locks which ensure that only one worker can enter to certain function simultaneously. With the django-pglock package this was rather easy:
import pglock with pglock.advisory("customer_email_notification_lock"): send_customer_email()
We really wanted to avoid similar mistakes elsewhere in our platform. We were working with many payment service APIs and they had a feature called idempotency header to avoid charging customers multiple times.
It was annoying that our email provider Sendgrid didn’t have such a feature. I spend a bit of time and found one transactional email service which actually supports this called Brevo.
Sending idempotent emails through Brevo 🔒 💌
You can create fancy email templates in Brevo dashboard but for the sake of example let’s keep things simple here. First you need to sign-up for an account and acquire API key. This shouldn’t take more than 10 minutes. After that you can start sending idempotent emails like this:
curl --request POST \ --url https://api.brevo.com/v3/smtp/email \ --header 'accept: application/json' \ --header 'api-key:YOUR_API_KEY' \ --header 'content-type: application/json' \ --data '{ "sender":{ "name":"Sender Sean", "email":"[email protected]" }, "to":[ { "name":"John Doe", "email":"[email protected]" } ], "headers": { "idempotencyKey": "YOUR-UNIQUE-IDEMPOTENCY-KEY-HERE" }, "subject":"Hello world", "htmlContent":"<html><head></head><body><p>Hello,</p>This is my first transactional email sent from Brevo.</p></body></html>" }'
YOUR_API_KEY
+ YOUR-UNIQUE-IDEMPOTENCY-KEY-HERE
with your configThis will then create a following email into the sender address. In order to replace the
t-sender-sib.com
in the sender address your need to setup DKIM and DMARC by following their documentation.Brevo has 15min TTL on their idempotency guarantee which means that in next 15 minutes their platform will only allow 1 email to go through and the next attempts will result in error like this:
{ "code":"duplicate_parameter", "message":"Email for the idempotency key has already been processed" }
This doesn’t prevent all possible errors but 15minutes is enough to prevent most mistakes.
Next thing you need to do is to verify your sender address and you’re good to go 💪. If you don’t want to write your own HTML you can use their templating online tool as well.
Pricing of Brevo 💰
Brevo has very generous free-tier 😘. Even the business tiers are priced in very competitive way so for example you can send 20k emails per month for 19€ and their platform supports WhatsApp and SMS messages as well.
I’m using Brevo in my own products and if you sign-up through following link you can gift me free email quota in their platform. Thanks a lot, this really helps my blog 🙇
Brevo allows sending 300 emails per day / 9000 emails per month for free so it’s pretty amazing product for emerging products and startups!
👉 Get your free idempotent emails here.
Alternatives
If you know other platforms which can do this let me know by email 🤝.