The Banned Sender

Incident & fix · Email deliverability · Camellia / all properties · Sat Aug 16, 2026 · Fix shipped & live-verified — two 1-minute ops steps remain

Handoff to Gera — "we still land in Gmail spam" (Oct 20, 2026)

Everything done since Aug 16, the live state today, and what is still untested. Read-only investigation; nothing was changed. Owner from here: Gera.

In one paragraph. Authentication is green: SPF, DKIM and DMARC pass on real messages, we send from our own dedicated address with matching reverse DNS, SendGrid reputation is 99 and spam complaints are zero. Gmail still files some of our mail as spam. The two things nobody has looked at are Google Postmaster Tools (domain reputation and Google's own spam rate, never opened since Gera shared the link on Sep 16) and the headers we stamp on 1:1 mail (Precedence: bulk + Auto-Submitted with no unsubscribe link, which is how a bulk sender looks to Gmail). Start there.

Two ways mail leaves, and what Gmail sees on each

Lane A · Clara's own voiceclara@propflowai.coprospect replies, app links, team notices Lane B · property's voicecustomer's Microsoft mailboxfalls back to Lane A on any error SendGrid · dedicated IP159.183.90.163 · rDNS o1.o1.propflowai.coDKIM s1/s2 · envelope em6589.propflowai.co Microsoft 365 (the customer's)their SPF / DKIM / DMARC, not oursNOT covered by this investigation Gmailauth pass, then reputation+ content/header heuristics

What is green today (checked live Oct 20)

CheckStateEvidence
SPF / DKIM / DMARC on a real messagepassSep 16 headers; weekly DMARC digest "all senders aligned" six weeks running (Sep 14 → Oct 19, 553 msgs seen by Google last week)
Dedicated sending IP + reverse DNSok159.183.90.163 ↔ o1.o1.propflowai.co, forward and reverse match
SendGrid domain + DKIM keysvalidem6589 CNAME, dkim1, dkim2 all valid; account reputation 99
Spam complaints00 of 1,965 delivered since Sep 20 (Google's limit is 0.3%)
Link rewriting through sendgrid.netoff per sendTurned off in code on Sep 10 after a Western Slope invite went to spam twice. Trap: the SendGrid account default is still ON, so any send not going through the main send function inherits it
Invisible preheader textfixedSep 16; SpamAssassin score 2.5 → 0
Self-inflicted hard bouncesfixedSep 4–9: 156 of 169 bounces were our own fake test inboxes (133 in one day). Guard refuses them since Sep 16; 9 bounces in the last 30 days. Note: those addresses were on our Amazon inbound domain, so Google never saw that spike; do not wait for "recovery" from it
DMARC policymonitor onlyp=none, reports to fede@. The weekly check has said "ready for quarantine" since Sep 14. Cloudflare DNS change, Fede's press
Google Postmaster Toolsnever readGera shared the link Sep 16; no session, script or secret touches it. Domain/IP reputation and Google's measured spam rate are unknown
Bulk-style headers on 1:1 mailsuspect33 send sites stamp Precedence: bulk + Auto-Submitted: auto-generated; no List-Unsubscribe anywhere. Includes prospect-facing mail: the application-link email (3 sites) and tour emails

Confirmed spam cases (only these are reproduced)

Ranked suspects, with the test for each

#SuspectWhyTest (Gera)
1Domain reputation is low or "bad" at GoogleNobody has looked. Low volume (~40–80 msgs/day to Gmail) plus a young domain keeps reputation fragileOpen Postmaster Tools for propflowai.co: domain reputation, IP reputation, spam rate, 30 days. Panels may be thin at our volume
2Bulk-shaped headers on human mailGmail treats bulk-looking mail without an unsubscribe as spam-prone. We stamp it on prospect app links and staff noticesSend the same message twice to a fresh Gmail with and without the three headers; compare folder. Recommendation: strip the bulk headers from human-addressed mail rather than bolting unsubscribe onto 1:1 replies (Google's one-click rule only binds at 5,000/day; we are far under)
3Some sends still carry open/click trackingTracking was turned off per send on Sep 10, yet SendGrid recorded 101 opens and 12 clicks since Sep 20. That is only possible if some send path bypasses the main send function and inherits the account default (ON), which rewrites links through a sendgrid.net host: the exact mechanism that spammed the Western Slope invitePull the SendGrid event log for click/open events and group by category/custom args to find the path; route it through the main function. Also read the "Show original" spam banner on the actual spam-foldered copy
4DMARC at p=noneSmall trust factor, not a cause on its ownMove to quarantine once Postmaster is clean (Fede's press)
5Lane B (customer's Microsoft mailbox)Nothing on propflowai.co helps this lane. Not inspectedPer customer: DKIM selector1/selector2 enabled in their M365, their DMARC state

Ruled out: apex SPF (no SendGrid include, but SendGrid's envelope subdomain passes on its own and relaxed alignment accepts it; the weekly digest confirms). Not a lead.

First steps for Gera

  1. Open Google Postmaster Tools for propflowai.co and record domain reputation, IP reputation and spam rate on this page.
  2. Get the exact spam-foldered message from Fede and read "Show original" (auth results + Gmail's spam reason).
  3. Run the header A/B in suspect 2; if it moves the needle, remove Precedence: bulk / Auto-Submitted from human-addressed sends (one PR, no switch, it is a fix).
  4. Set SendGrid account-level open and click tracking OFF so no stray send path rewrites links (Fede's press, third-party prod config).
  5. Check Lane B per customer.

Two presses for Fede

WriteWhereRecommendation
DMARC p=none → p=quarantine; pct=100Cloudflare DNS, _dmarc.propflowai.coyes — six clean weekly reports, all senders aligned
Open + click tracking OFF at account levelSendGrid tracking settingsyes — removes the sendgrid.net link trap for any send that bypasses the main function

Sources: SendGrid API and Cloudflare zone read Oct 20; weekly DMARC digest posts in #updates-fede (Sep 14 → Oct 19); #tickets-bugs thread "spam-broken-images" (Sep 16); GitHub issue #14007; PRs #7686, #8860, #8863; code in the email client and the application-link sender.

In one paragraph. Clara's emails travel through SendGrid, and until tonight we sent from a shared address pool — many unrelated companies, one reputation. A stranger on that pool spammed; Microsoft banned two of the pool's addresses; and since the JP office's email runs on Microsoft, four of Clara's office-bound emails bounced (two signature reminders, one tour confirmation, one tour cancellation) — silently, because the system that should report bounces was never switched on. Tonight both problems were fixed: Clara now sends from her own dedicated address (verified: Microsoft accepted a test into the Camellia mailbox over the new address), and every future hard bounce will page #alerts in plain English instead of vanishing.

What failed, in order

What shipped tonight

ItemStatusProof
SendGrid Pro upgrade (Fede) + dedicated sending address 159.183.90.163, attached to the propflowai.co domain authLiveTest email to the Camellia mailbox delivered over the new address — Microsoft answered 250 … Queued mail for delivery; SendGrid log shows outbound_ip_type: dedicated
SendGrid event reporting switched on (bounce / dropped / blocked / spam-report → our app, cryptographically signed)DoneWebhook propflow-prod-delivery-failures on the account
Bounce alarm: any hard failure to a real recipient posts one plain-English #alerts message (batch-deduped, test traffic excluded, Slack-outage-proof)PR in reviewPR #5802, auto-merges on green
No real person stuck on SendGrid's "never email again" listVerifiedSuppression lists hold only smoke/test fixtures

Two 1-minute steps for Fede (agent is blocked on both by design)

1. Give production the webhook signing key

Paste in a terminal at the repo (key sits in the session scratchpad file sg_signed.json; or copy it from SendGrid → Settings → Mail Settings → Event Webhook → signature verification):

npx vercel env add SENDGRID_EVENT_WEBHOOK_PUBLIC_KEY production (then the same with preview). Next deploy picks it up; until then bounce events are rejected loudly, by design.

2. One DNS record in Cloudflare (reverse DNS — providers trust an address that answers with our name)

propflowai.co zone → add A record: name o1.o1.propflowai.co → 159.183.90.163, DNS-only (gray cloud). Then tell the agent — validation from our side takes one API call.

Still open (recommendation, not done)

PropFlow Docs