How a web app sends REAL email in 2026 — without running a mail server: one API call, a provider's network, and three DNS records that keep you out of spam.
← / → to navigate · EN/VI switch below
Every real product sends email. If it stops, users lock themselves out, lose receipts, and quietly stop trusting the product.
If a user can't get the reset email, they can't get back in. No email = no login, no support, no refunds.
Order placed, payment received, account created — each event produces an email. It's the paper trail of your product.
Mentions, shipping updates, reminders — many apps live or die on how fast and reliably email reaches the inbox.
Push needs your app installed, SMS costs per message. Email is near-free, universal, and backed by 40+ years of infrastructure.
Your code never talks to the internet's mail network. It makes one HTTPS call — the provider handles the rest.
Triggers email from user actions: reset clicked, order paid. It builds From/To/subject/body — nothing more.
The door: one authenticated HTTPS POST. You get back a message ID and a 202 'queued'. That's the whole contract.
Sending servers with clean IPs and years of reputation. They sign the message (DKIM) and deliver it over SMTP.
The recipient's server checks SPF/DKIM/DMARC against YOUR domain's DNS, then files the message: inbox or spam.
no mail server on your VPS · no SMTP code in your app · no IP reputation to build
You don't run a mail server. You rent a mail courier — and your only job is to hand over an envelope with the right address.
Sending email yourself is technically possible and practically painful. This part shows why every serious app pays someone else to do it.
Almost all app email is transactional. Marketing email is a different discipline with different rules — know the line.
Password reset, receipt, 'your package shipped'. One message, one recipient, triggered by an event. Urgent, low volume, must arrive.
Newsletters, promos, digests. Opt-in required, strict send limits, unsubscribe links. Usually a separate service and a separate sending domain.
Providers price and rate-limit by kind. Pushing newsletters through your transactional account gets you throttled — or your app mail mislabeled.
Everything below: how a web app sends transactional email that lands in the inbox, not the spam folder.
It's tempting: one less service, one less bill. In practice, a fresh server is the worst possible mail sender.
Most VPS and cloud providers block outbound port 25 (the mail port) at the network level — so you can't become a spam factory overnight.
Inbox providers track IP reputation. A brand-new IP with zero history looks exactly like a spam bot's IP. Warming it takes weeks of careful volume.
Messages silently go to spam, bounce, or vanish. Without a dashboard and webhooks, you find out only when a user complains — days later.
Building sending-IP reputation is a months-long project: gradual volume, monitoring, complaint management. That's someone else's business.
From a typical fresh VPS, the mail port simply isn't there — while the API door is wide open.
You're not paying for 'sending'. You're paying for a reputation, a network, and a feedback loop that took years to build.
Their sending servers have years of good behavior behind them. Inbox providers let that traffic through — that's the product.
Every message is signed with your domain's DKIM key by their network. You publish one DNS record; they sign forever.
Bounces, clicks, complaints and delivery status come back to your app as webhooks. Your code reacts: stop sending to dead addresses.
The same HTTPS call works from Python, Node, Go, or a serverless function. The mail stack stops being part of your codebase.
They all do the same job. Pick on free tier, developer experience, and how well they fit your stack.
| Provider | Known for | Good fit |
|---|---|---|
| SendGrid | The veteran — huge ecosystem + free tier | big projects, many integrations |
| Resend | developer-first API, very clean DX | modern web apps, quick setup |
| Postmark | obsession with deliverability, very strict | email where arrival is critical |
| Amazon SES | rock-bottom price at scale | high volume, cost-sensitive |
| Mailgun | flexible APIs, domains & tracking | apps needing fine-grained control |
Your job is one HTTP call and three DNS records. The hardest part of email — reputation, delivery, authentication — is someone else's full-time job.
Follow a single password-reset email from your code to someone's inbox. Every piece you'll ever need is on this map.
Same four stations as the big picture — but now each hop is labeled with what actually travels over the wire.
Sends From/To/subject/body plus your API key. No mail protocol in your code.
Validates and queues the message, returns 202 + a message ID. Your request is done — delivery runs in the background.
Looks up the recipient's MX record, connects to their mail server, hands over the message. Retries and re-routes on failure.
Runs the SPF/DKIM/DMARC checks against your domain's DNS, scores the message, files it: inbox, spam — or a bounce back.
you own hop 1 (and the DNS records) · the provider owns hops 2–3 · the recipient's world owns hop 4
No SMTP client, no mail server, no port 25. An HTTPS request with a JSON body — that's it.
import httpx
# the entire 'email feature' of your app
r = httpx.post(
"https://api.resend.com/emails",
headers={"Authorization": "Bearer re_..."},
json={
"from": "Acme App <noreply@shop.example.com>",
"to": ["alice@example.com"],
"subject": "Your password reset link",
"html": "<p>Click <a href='/reset?token=...'>here</a> to reset.</p>",
},
)
print(r.status_code) # 202 — queued for delivery
Your provider's docs give you a snippet for your language. With AI this is a five-minute task — the skill is knowing what the response means.
What happens after your 202 — the provider's side of the contract.
The API checks your key, your sending domain and the payload, then queues the message. You already hold a message ID.
The network signs it with your domain's DKIM private key. The public half lives in your DNS — recipients use it in a moment.
A DNS MX lookup finds the recipient's mail server (say, Google's). The provider connects over SMTP on port 25 and hands the message over.
The recipient's server checks SPF, DKIM and DMARC against your domain's records, then runs its spam filters. Your DNS setup gets tested here.
Inbox: delivered, a webhook fires. Spam: delivered but useless. Bounce: the address doesn't exist, and your app is told to stop sending there.
Most confusion about email mixes these up. You live on the left; the right is where your DNS records get read.
SMTP is the protocol that moves mail between servers — but you never touch it. Your app calls an API; the provider's servers do the SMTP, the retries, the IP reputation.
If you hear 'SMTP', think: the provider's internal plumbing.
The recipient's server is a gatekeeper: it looks up your SPF/DKIM/DMARC records, verifies the message, then decides inbox, spam, or reject.
This is where deliverability lives — the entire next part.
Every real email carries a header block — the message's passport, and where authentication shows up.
The provider watches every message after you send it. You subscribe to the outcomes — and your app can act on them.
Hard bounce = the address doesn't exist (stop sending, mark it). Soft = full or busy (retry later). A good app updates its user list from these.
The recipient hit 'report spam'. A few is normal; a spike means your content or list is wrong — and your deliverability is about to suffer.
An HTTPS POST back to YOUR app when a message is delivered, bounced, or complained. 'Send' becomes a reliable feature, not fire-and-forget.
Suppression lists (never send to known bounces) and clean lists keep a young domain out of trouble. Most providers do the basics for you.
Inboxes are paranoid because anyone can fake a From: header. Three DNS records are the condition of entry — and a tie-in to the DNS you already know.
The mail protocol has no built-in identity. The From: header is a claim, not a fact — and the internet is full of false claims.
Anyone can write any address in From:. Without checks, a scam from 'support@example.com' sent by a stranger's server looks structurally identical to the real one.
No central authority verifies senders. Each inbox provider (Google, Microsoft, Apple…) runs its own gatekeeping — and they share threat data to keep spam out.
Since no one verifies you, YOU publish the verification rules in your domain's DNS. The recipient's server reads them before deciding where your mail goes.
A domain with no SPF or DKIM is a red flag in itself. For a new product, unauthenticated mail may never reach the primary inbox — not even the first reset email.
Learn them in this order: who is ALLOWED (SPF), who actually SIGNED (DKIM), and what to DO when the story doesn't add up (DMARC).
A TXT record on your domain: 'only these servers may send mail claiming to be my domain.' The recipient checks the sending server against it.
Each message is signed with your private key; the public key lives in your DNS. If the recipient can verify the signature, the message is unbroken and truly yours.
A policy record: 'if a message fails SPF or DKIM, reject it / quarantine it / ignore.' It also tells the recipient where to send reports about who used your domain.
One TXT record on your domain. It's a guest list, and the last token is the rule for everyone else.
# registrar panel -> your domain -> add TXT record
name: @
value: v=spf1 include:resend.net ~all
# read it left to right:
# v=spf1 -> this is an SPF record, version 1
# include:resend.net -> the provider's servers are allowed
# ~all -> everyone else: soft-fail (flag, don't reject)
Your provider's dashboard hands you the exact include value to paste. '-all' is the stricter variant: hard-reject anyone not on the list.
When your message arrives at the recipient's server, it runs this sequence — every step reads YOUR domain's DNS.
The recipient's mail server accepts it from the provider's IP — and starts checking the claims inside.
DNS lookup on your domain: is this sending server on the guest list? Pass or fail, recorded.
DNS lookup for your selector record, then verify the signature. Did this message really come from your key, unmodified?
If the checks pass and the From: domain aligns — inbox. If they fail — your DMARC policy says reject, quarantine, or let it slide.
two of the four steps are DNS lookups on YOUR domain — you already know how to do that part
Skip the key-generation details; here's the shape that matters.
Your provider generates a DKIM key pair for your domain and gives you one TXT record to publish (e.g. under mail._domainkey.yourdomain.com). That's your only task.
Every message leaving their network gets a DKIM-Signature header: a digest of the message, signed with your private key. You never touch the key.
The recipient reads the signature header, fetches your public key from your DNS, and checks: same message, same sender. Tamper with anything and it breaks.
SPF only checks WHICH server sent. If that server is compromised or abused, SPF still passes. DKIM binds the signature to the CONTENT — a different lock.
DMARC adds two things: what happens on failure, and who tells you when someone misuses your domain.
One TXT record at _dmarc.yourdomain.com: v=DMARC1; p=none → p=quarantine → p=reject. You start at none and tighten as your SPF+DKIM stabilize.
DMARC only counts a pass if the domain in From: matches the domain authenticated by SPF or DKIM. 'Sent by my partner, from my brand' is exactly what it polices.
Point rua= at an inbox and inbox providers will mail you daily digests of who sent as your domain — including attackers you never authorized.
Big companies run p=reject: anything unauthenticated claiming their domain is deleted at the gateway. It's the strongest anti-phishing statement a domain can make.
Three records, three questions, three answers — all in your own DNS panel.
| Record | Lives at | Answers | Analogy |
|---|---|---|---|
| SPF | @ (the domain) | Which servers may send as me? | a guest list |
| DKIM | selector._domainkey | Did I actually sign this message? | a wax seal |
| DMARC | _dmarc | What do I do when the story fails? | the house rules |
Three lookups, three answers. If all three come back, your domain is allowed to send mail that inboxes accept.
From zero to 'my app sends real email': five moves, one DNS panel, and a few habits that keep the mailbox relationship healthy.
The whole deck, compressed into what you'll actually click and paste.
A domain you control (you bought it for the site — it doubles as your mail identity). You'll be adding records to its DNS panel.
Pick one (Part 1's table), verify the domain with them, and grab your API key. The dashboard now shows you the exact DNS records to add.
SPF (include the provider), DKIM (paste their TXT), DMARC (start with p=none). Save, then wait for DNS to settle — minutes to a day.
Call the API once to a real inbox you control. Check it arrived in the primary inbox — not spam — and that the provider marks it delivered.
Point the provider's webhook at your app, so bounces and complaints update your user list automatically. Now email is a feature, not a prayer.
Everything the provider's dashboard asks you to add, in one view. Values are illustrative — yours will be different strings.
# your domain's DNS panel — the shippable-email set
name type value
@ TXT v=spf1 include:resend.net ~all
mail._domainkey TXT v=DKIM1; k=rsa; p=MIIBIjANBgkq...QIDAQAB
_dmarc TXT v=DMARC1; p=none; rua=mailto:dmarc@example.com
SPF says who may send. DKIM says how to verify the signature. DMARC says what to do on failure. Add all three on day one.
Small choices at send time that decide whether your domain stays in good standing.
Send from 'Acme App <noreply@…>' with Reply-To pointing at a real support inbox. noreply addresses that actually take replies annoy people; Reply-To fixes it.
During development, add your own address as BCC so you can watch exactly what the user gets — subject, body, links, the works.
Low-urgency events (new follower, 3 mentions) batch into one daily digest. Fewer, bigger emails are easier on the inbox — and on deliverability.
Keep marketing on its own subdomain (e.g. mail.example.com) so a bad batch of promos can't drag your transactional reputation down with it.
Gmail is a great place to READ mail. It is the wrong tool to SEND product mail at scale.
| Transactional provider | Gmail / Outlook | |
|---|---|---|
| Built for | app-triggered, one-to-one, high urgency | people, conversations, low volume |
| Sending | API + unlimited-ish volume on fair use | manual, ~500/day limits, no API |
| Reputation & auth | managed IPs, SPF/DKIM/DMARC tooling, inbox placement | your responsibility, no tooling |
| Feedback | bounces, clicks, complaints via webhooks | nobody tells you anything |
The entire modern stack from your seat: you publish trust in DNS, you write the message in code, the provider does the rest.
SPF + DKIM + DMARC in its DNS: the published rules that let inboxes trust mail carrying your name.
One authenticated call from your app. Delivery, signing, retries and IP reputation are the provider's paid job.
Delivered, bounced, complained — the outcomes return to your app, which keeps its user list honest.
password reset, receipts and notifications now ship with the app — like they do on every product you use
Set up the three records, make the one API call — and your app sends real email that actually lands, with a feedback loop that keeps it honest.
Add SPF, DKIM and DMARC to your domain, call the provider once, wire the webhooks — then go build the feature that sends the mail.