Skip to main content

Send Email Without Landing in Spam

Share

Share this page

Pass it to someone who needs it.

Star on GitHub

Key takeaway: Send mail through a provider, not your server

The moment you have accounts and payments, your app has to send mail: a receipt, a verification link, a password reset, a note that someone replied. Send it from your own server and most of it lands in spam or never arrives. This chapter gets your app's mail actually delivered.

10.4.1A real app sends its own mail

Every action your app takes on someone's behalf can owe them a message. A receipt after a charge, a link to verify an address, a reset when they are locked out, a note that someone replied. That is transactional email, the automatic one-to-one mail your app sends in response to a single user action. It is not a marketing blast to a list; it is one message, to one person, triggered by one thing they did.

Your auth provider already sends its own login and reset mail. This chapter is about everything else your app owes the user.

10.4.2Never send mail from your own server

Getting mail into an inbox is a specialty of its own. Send it straight from your app server and inbox providers treat an unknown machine as a likely spammer, so your receipts land in junk or vanish. Hand it to an email provider like Postmark, Resend, or Amazon SES, whose entire job is reaching the inbox.

One call does it, and your app never speaks the low-level mail protocol itself.

// One call to your provider sends the receipt. // Your app never talks to SMTP; the provider does. await resend.emails.send({ from: '[email protected]', to: user.email, subject: 'Your receipt', html: renderReceipt(order), })

Until you own the domain you will send from, none of this can be tested for real. So point your app at a local mail catcher like Mailpit, and read what it traps instead of sending it.

Straight from your server, mail lands in spam; through a provider, it reaches the inbox.

10.4.3Deliverability is the part you outsource

Inboxes only trust mail they can prove came from you, and three domain records do that proving. SPF and DKIM declare who may send for your domain and stamp each message with a signature only you could make. DMARC tells inboxes what to do when one of those checks fails. Gmail and Yahoo now require it from anyone sending at volume, so two records is an unfinished setup.

Your provider generates all three for you. You add them once to your domain's DNS, the settings that tell the internet how your domain works. Start the DMARC record at p=none, which asks for reports without changing how your mail is treated, and only tighten it once those reports come back clean.

From then on the provider also absorbs the bounces and spam complaints that would otherwise wreck your sending reputation.

10.4.4Notify on other channels, but do not spam

The same rule covers every other channel. In-app alerts, push, and SMS all go through a provider built for them, never a pipe you run yourself. The harder question is not how to send but whether to. Pick the few events that genuinely deserve interrupting someone, a payment, a reply, a security alert. Let the rest live quietly in the app.

Watch out: every extra notification trains people to ignore all of them. A channel your users mute is worse than one you never built.

This prompt wires it all to a provider and picks the events worth sending:

Ready prompt
Act as a senior engineer wiring my app's transactional email through a provider. Do not build SMTP or send from our own server. Read my architecture map and my decision log first, so this fits the structure and reverses no choice already recorded there. 1. Pick an email provider (Postmark, Resend, or Amazon SES) and wire it behind a vendor adapter I own, so one call sends a message and the provider stays swappable. Its keys go in .env and .env.example, never in code. 2. Set up domain authentication: generate the SPF, DKIM, and DMARC records, tell me exactly what to add to our DNS, start DMARC at p=none, and confirm mail is signed. 3. List which events should send mail (receipt, verification, reset, reply) and which should not, so we never spam users. 4. For any other channel I need (in-app, push, SMS), name the provider and the events worth sending, and skip the rest. Give me the provider choice and the DNS records first, then the send code. Record the provider choice as one line in my decision log. If you need the full reasoning behind this step, read https://zalt.me/guides/vibe-coding/harden/sending-email-and-notifications My app: what it sends, to whom, on what events:

Do this now: paste the prompt, wire your app's mail through one provider, and add its SPF, DKIM, and DMARC records to your domain before you send a single real message.

Mahmoud Zalt

Mahmoud Zalt

Software engineer, 16+ yrs · built Sistava.com in 3 months, idea to production, using these methods

Resources
Star on GitHubContribute
Donate

Support my work

A small tip keeps the free work coming.

© 2026 Mahmoud Zalt. Free to read, not to republish.
Copyright & license