Dashboard

Why Emails From Your AI-Built App Go to Spam

If mail from your AI-built app lands in spam, the send is working and the receiver does not trust it. A header-first diagnostic order for SPF, DKIM, DMARC and reputation.

Steve Jefferson
Steve Jefferson
Developer Advocate
19 September 20261 min read

Why Emails From Your AI-Built App Go to Spam

Password resets landing in spam is almost never a code problem, which is why asking an AI to "fix the email sending" rarely helps. The send is working. The receiving mail server is choosing not to trust it, and it makes that choice using three DNS records and a reputation history your domain does not have yet. Here is the order to check things in, fastest first.

Start by reading the headers, not the code

Send yourself a test message at a Gmail address, open it, and use "Show original". You want three lines:

text
spf=pass       (google.com: domain of bounce@yourapp.com designates 1.2.3.4 as permitted sender)
dkim=pass      header.i=@yourapp.com header.s=s1
dmarc=pass     (p=NONE sp=NONE dis=NONE) header.from=yourapp.com

Any of those reading fail or none tells you exactly which of the next three sections to read. If all three say pass and mail still lands in spam, skip to the reputation section, because your DNS is fine and the problem is behavioural.

SPF: does this server have permission to send as you

SPF is a TXT record on your domain listing who may send mail for it. It breaks in a specific way for AI-built apps: the app gets wired to a sending provider, the provider's include is added, and then a second provider is added later for marketing mail and someone publishes a second SPF record. Two SPF records is a permanent fail, not a merge. There must be exactly one, with both includes inside it.

text
v=spf1 include:_spf.provider-one.com include:_spf.provider-two.com ~all

The other common break is the ten lookup limit. Each include costs a lookup, and nested includes count. Past ten, SPF returns permerror and receivers treat it as a fail.

DKIM: is the message signed and unmodified

DKIM publishes a public key at a selector on your domain and signs each message with the private half. The failure here is usually that the provider generated keys and printed the CNAME records, and whoever set up the app pasted them into the wrong zone, or pasted them at the apex instead of the selector subdomain. Verify by looking up the selector directly rather than trusting the provider dashboard's green tick, which often only checks that the record exists rather than that it resolves publicly.

bash
dig +short s1._domainkey.yourapp.com CNAME
dig +short s1._domainkey.yourapp.com TXT

DMARC: the record almost nobody adds

This is the one most AI-built apps are missing outright, and since 2024 the large mailbox providers have expected it for bulk senders. Start in monitor mode so nothing you send gets rejected while you are still learning what your own domain sends:

text
_dmarc.yourapp.com  TXT  "v=DMARC1; p=none; rua=mailto:dmarc@yourapp.com"

Leave it on p=none for a couple of weeks and actually read the aggregate reports. They will show you sources sending as your domain that you forgot about, which is usually a billing provider or a help desk tool. Only tighten to quarantine once those are all passing.

Reputation: the part DNS cannot fix

A domain that has never sent mail has no reputation, and a domain that sends its first 5,000 messages in one afternoon builds a bad one. Three habits matter more than any record:

  • Separate your transactional and marketing sending onto different subdomains, so a bad campaign cannot poison password resets.

  • Never send to an address that has bounced hard. Repeatedly hitting dead addresses is one of the strongest negative signals there is.

  • Put a real unsubscribe header on anything that is not strictly transactional, and honour it immediately.

Sending from a subdomain such as mail.yourapp.com rather than the apex is also worth doing early, because it isolates the reputation of your app's mail from whatever else the domain is used for. If you have not moved off a platform subdomain yet, do that first: moving the app onto your own domain has to happen before any of this is meaningful.

The order to work through it

Symptom in headers

Most likely cause

Fix

spf=fail

Two SPF records, or the provider's include missing

Merge into one record with both includes

spf=permerror

More than ten DNS lookups

Flatten or drop unused includes

dkim=fail or absent

Selector records in the wrong zone

Look up the selector with dig, not the dashboard

dmarc=fail, SPF and DKIM pass

Alignment: the From domain differs from the signing domain

Match the envelope and header From domains

All three pass, still spam

Reputation or content

Warm up slowly, split subdomains, clean the list

Frequently asked questions

Why do my emails go to spam only on Gmail?

Gmail weighs domain reputation and engagement more heavily than most receivers, so a new domain with no history is filtered there first. It is usually the earliest warning rather than a Gmail-specific bug.

Do I need DMARC if SPF and DKIM already pass?

Yes, for anything approaching bulk volume. The large providers have expected a DMARC record from bulk senders since 2024, and monitor mode costs you nothing operationally.

Will changing DNS fix it immediately?

The records propagate in minutes to hours, but reputation does not reset. If you have already sent a burst of mail that got marked as spam, expect days of consistent good sending before placement improves.

Should transactional email go from a subdomain?

Yes. It isolates password resets and receipts from marketing sending, so one bad campaign cannot stop people logging in.

How do I test all this before launch?

Send to addresses at several providers and read the headers on each, rather than checking one inbox. It fits naturally into the pre-launch testing pass, and if you have not set sending up at all yet, start with wiring up email sending in the first place. The wider build context is in the full guide to building an app with AI.

How did this land?

About the author

Steve Jefferson
Steve Jefferson

Developer Advocate

Steve builds something with Swarmz every week and writes up what worked, what broke, and what he'd do differently. Tutorials and hands-on guides are his lane.

Share

Get the next post in your inbox

One email a month. Product updates, engineering posts, and the best of Built with Swarmz.

I agree to receive emails about AI building tips and Swarmz product news. Unsubscribe any time.