DEV Community

Cover image for I Was Tired of Spam, So I Built My Own Temporary Email System
Osman
Osman

Posted on

I Was Tired of Spam, So I Built My Own Temporary Email System

This post is partly a mini-guide, partly a note to my future self who will probably forget how this worked.

Like many developers, I don’t want to give my main email address to random websites just to receive a one-time verification code (OTP).
Because let’s be honest:
you verify once… and then you receive newsletters forever.

So I built a simple inbound email tool using:

What This Tool Does (In Short)

  • Works like a temporary email inbox

  • Supports HTML and plain-text emails

  • Extracts OTP / verification codes

  • Forwards attachments if any

Before You Start: You Need Your Own Domain

Before anything else, you need your own domain.

SendGrid Inbound Parse does not work with free email domains (like Gmail, Outlook, etc.).
You must own a domain so you can control the MX records.

In my case, I simply bought a domain from Namecheap.

Once you have your domain:

  • You can point MX records to SendGrid

  • Create addresses like noreply@your-domain.com

  • Fully control inbound email routing

Without a custom domain, this setup is not possible.

Step 1: Adding SendGrid DNS Records to Your Domain Provider

SendGrid does not manage your DNS.
Instead, it tells you which DNS records you must add, and you are responsible for adding them to your domain provider (Namecheap, GoDaddy, Cloudflare, etc.).

This section shows how to correctly transfer DNS records from SendGrid to your domain provider.


First, lets check our DNS Records on SendGrid.

  1. Go to Settings/Sender Authentication
    Settings/Sender Authentication

  2. Click your domain
    Domain

In Manual Install section, SendGrid will display a list of DNS records such as:

  • CNAME records

  • TXT record

  • Verification status (red / green)
    Sendgrid DNS Records


⚠️ These records are instructions, not active DNS entries. We are gonna use these records, so take a note for next step.

Step 2: Open your domain provider’s DNS panel

For example, in Namecheap:

  1. Go to Domain List
    Namecheap Domain List
    You will see now your all domain list from provider.

  2. Click Manage
    Namecheap Manage

  3. Click Advanced DNS
    Namecheap Advanced DNS
    Now you will see your Host Records

  4. Copy records exactly as shown from your Sendgrid DNS Records
    For each record shown in SendGrid, create a matching record in your domain provider. Don't forget to add dots every CNAME Values.

  5. Save and wait for DNS propagation
    DNS changes are not instant.

  6. Verify inside SendGrid
    Once DNS propagates:

  • Return to Sender Authentication

  • Click Verify

  • Records should turn green


This means SendGrid can now authenticate emails sent from your domain.

Step 3: DNS Configuration (Important!)

This part trips people up... including me 🙃

MX Record (Domain Provider, e.g. Namecheap)

Add this MX record to your domain provider:
Namecheap MX Record

⏳ DNS propagation can take a few minutes to a few hours.
Yes, even if SendGrid says “Verified”.


You can check with:

  • Record type: MX

  • Domain: your-domain.com

Step 4: SendGrid Inbound Parse Setup

In SendGrid:

  1. Go to Settings/Inbound Parse
    Settings/Inbound Parse

  2. Click Add Host & URL
    Add Host & URL

  3. Choose your Domain
    Domain

  4. Set the Destination URL:
    Destination URL

  5. And then press Add


SendGrid will now expect emails sent to @your-domain.com and forward them to your Flask app.

Step 5: Flask App – Receiving Emails

Here’s the core logic. (Full project is on my GitHub)

from flask import Flask, request
from bs4 import BeautifulSoup
import re, base64
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail, Attachment, FileContent, FileName, FileType, Disposition

from_email = 'noreply@your-domain.com'
to_email = 'YOUR_EMAIL'
API = 'YOUR_API_KEY'

app = Flask(__name__)
otp = ''
_to = ''
sg = SendGridAPIClient(API)
Enter fullscreen mode Exit fullscreen mode

Why these libraries?

  • Flask → Webhook receiver

  • BeautifulSoup → Extract text from HTML emails

  • re → OTP detection

  • SendGrid SDK → Forward emails

  • base64 → Handle attachments

Step 6: Inbound Email Endpoint

@app.route('/email', methods=['POST'])
def email_receiver():
Enter fullscreen mode Exit fullscreen mode

This endpoint is called by SendGrid, not by a browser.

Reading Email Metadata

print('From:', request.form['from'])
print('To:', request.form['to'])
print('Subject:', request.form['subject'])
Enter fullscreen mode Exit fullscreen mode

Useful for debugging and filtering later.

Step 7: Handling HTML Emails

if 'html' in request.form:
    soup = BeautifulSoup(request.form['html'], 'html.parser')
    text = soup.get_text()

    if "Your verification code is as mentioned below" in text:
        match = re.search(r'verification code.*?(\d{6})', text)
        _to = request.form['to']
        otp = match.group(1)
Enter fullscreen mode Exit fullscreen mode

Once an email hits the endpoint, the service first converts any incoming HTML content into plain text. This makes the message easier to analyze and avoids dealing with messy markup.

After that, the text is scanned for a 6-digit verification code, which is typically how most OTP emails are structured. When a match is found, the code is extracted and stored so it can be forwarded or used immediately.

This approach keeps the logic simple and flexible, while still covering the majority of real-world verification emails.

Step 8: Handling Plain Text Emails

elif 'text' in request.form:
    text = request.form['text']
    if "Verification Code:" in text:
        match = re.search(r'\b\d{4}\b', text)

    _to = request.form['to']
    otp = match.group() if match else ""
Enter fullscreen mode Exit fullscreen mode

This covers emails without HTML (surprisingly common).

Step 9: Handling Attachments

attachments = []
for file_key in request.files.keys():
    file = request.files[file_key]
    file_bytes = file.read()
    encoded = base64.b64encode(file_bytes).decode("utf-8")
Enter fullscreen mode Exit fullscreen mode

Attachments are:

  • Read

  • Base64-encoded

  • Re-attached to the forwarded email

Step 10: Forward Everything to Main Email

message = Mail(
    from_email = from_email,
    to_emails = to_email,
    subject = request.form['to'],
    plain_text_content = text
)

for attachment in attachments:
    message.add_attachment(attachment)
Enter fullscreen mode Exit fullscreen mode

When forwarding the email to my main inbox, I keep things intentionally simple but useful. I use the temporary email address itself as the subject, so I can immediately see which site the message originally came from without opening the email.

Along with that, I forward the entire email content, including any attachments if they exist. This way, nothing is lost in translation — whether it’s a verification message, a receipt, or an attachment-based confirmation, everything arrives exactly as it was received.

Final Step: Send & Done

sg.send(message)
return 'OK', 200
Enter fullscreen mode Exit fullscreen mode

If SendGrid receives 200 OK, you’re golden 🟢

Why I Built This?

I simply don’t want spam. The moment you give your real email address to a random website, your inbox slowly turns into a digital landfill.

I also don’t fully trust every site that asks for my email. Most of the time, I don’t even want to use their service long-term. I just need one thing: the OTP or verification code so I can move on.

This project was my way of creating a buffer between “random websites” and my real inbox. A disposable entry point that catches verification emails, extracts what I need, and forwards only the important part to my main email.

Full Source Code

GitHub logo Osman-Kahraman / mail_otp_receiver_base

Self-hosted disposable email system using SendGrid Inbound Parse and Flask. Receive OTP codes, parse emails (HTML/Text), and forward them with attachments without exposing your main email address.

mail_otp_receiver_base

mail_otp_receiver_base is a self-hosted disposable email (temp mail) system built with SendGrid Inbound Parse and Flask.

It allows you to receive emails on your own domain, automatically extract OTP / verification codes from both HTML and plain-text emails, and forward them — along with attachments — to your main inbox, without exposing your real email address to spammy websites.

I built this mainly to avoid giving my personal email to random services.
I’m also keeping this repo as a reminder for my future self — because I will forget how I set this up.


Features

  • Self-Hosted Temp Mail – Works like popular temporary email services
  • OTP Extraction – Automatically parses verification codes using regex
  • HTML & Text Support – Handles both email formats
  • Attachment Forwarding – Forwards attachments if present
  • Custom Domain Support – Full control via your own domain
  • Lightweight & Simple – Minimal setup, no database…

Deploying on Render (Sleeping Server? Still Fine)

This project can be deployed on Render, even using the free tier.

At first glance, Render’s free tier sounds risky because the server goes to sleep after a period of inactivity. Normally, that would raise concerns about availability and reliability. In this setup, however, it turns out to be a non-issue.

Incoming emails never depend on your application server being online. Thanks to the MX records, emails are routed directly to SendGrid, which fully handles mail delivery. By the time your Flask app becomes relevant, the email has already been accepted.

Your server’s only responsibility is post-processing. It parses the content, extracts OTP or verification codes, and forwards the data when available. Whether the server is awake, asleep, or restarting does not affect email delivery itself.

If the server happens to be asleep, nothing breaks. There is no “address not found” error, no rejected email, and no race condition waiting for a wake-up. The worst-case scenario is simply that parsing happens a bit later.

This separation of responsibilities is exactly why free-tier hosting works well here. Email delivery is handled by dedicated infrastructure, while your app adds logic on top. For a personal disposable email system or OTP-forwarding tool, this tradeoff is more than acceptable.

Closing Thoughts

This setup shows that not every backend needs to be online 24/7 to be reliable. By letting specialized services do what they are best at, you can simplify your architecture without sacrificing correctness.

Using Render’s free tier is not a compromise here. It’s simply a practical choice. The email pipeline remains stable, delivery is guaranteed, and your application adds value only where it matters.

Sometimes, the cleanest solution is the one that worries the least about uptime.

Lessons Learned

Email delivery and application logic should live in different layers. Once those responsibilities are separated, many common hosting concerns simply disappear.

Free-tier infrastructure can be perfectly viable when your system is event driven rather than request-driven. If nothing breaks when your server sleeps, then sleeping is not a problem.

Finally, designing around real constraints instead of fighting them often leads to simpler and more robust systems.

This post only explains the core logic.
If you’re tired of spam too, feel free to fork, improve, or just steal the idea :)

Happy hacking!!!

Top comments (2)

Collapse
 
elshadhu profile image
ElshadHu

That seems cool. I liked how you go step by step and explain what happens. Keep going gang.🙌

Collapse
 
osmankahraman profile image
Osman

Thank you ma g