Guides

What breaks when forwarded mail lands in a shared inbox

The envelope sender stops being the sender, the catch-all closes a loop your own-address check cannot see, and de-duplicating by similarity hides one-time passcodes.

Yowl handles all four of these, and nothing it receives is ever dropped. Sender taken from the From: header rather than the envelope, own-address recognition that includes our sending subdomain, exact-match de-duplication with an attachment fingerprint, and a bounded ingest window instead of a similarity score.

⭐ A duplicate is stored and marked, never discarded — and that is the rule the whole platform is built on. No code in Yowl deletes a message. Not a filter, not the AI, not a member tidying up. It is the reason “the email never arrived” is answerable every time rather than a shrug: the message is there, and Yowl can tell you whether it was filed, muted, deduplicated, or classified somewhere you were not looking.

Every failure below cost us a production incident first, and each one is the same shape — a plausible rule that silently removed something. That is why the rule is now the other way round. Here is what caused each.

Setting the forward up is a settings page. What happens after it starts flowing is where the interesting failures are, and all three of these cost us a production incident before we understood them.

The envelope sender stops being the sender

Every mail message carries two sender addresses: the From: header the reader sees, and the SMTP envelope sender — MAIL FROM, surfaced as Return-Path — which exists so bounces have somewhere to go.

For directly-delivered mail they usually agree, which is exactly why this bites. A forwarder must rewrite the envelope, because it is now the one responsible for handling bounces; the mechanism has a name, the Sender Rewriting Scheme. Service platforms do the same thing for their own reasons, encoding a per-message bounce identifier into the envelope (VERP).

So the envelope arrives looking like 0102abc…@send.somedomain.example and the From: header still says who actually wrote the message. We originally attributed the sender from the envelope, and it failed twice over: correspondents were recorded under a machine-generated bounce address, and replies were sent to it, which nobody reads.

The rule that works: the sender is the From: header, canonicalised — display name stripped, lowercased, so Brent@… and brent@… are one person. Fall back to the envelope only when there is no header at all, which in practice means synthetic mail you generated yourself.

Your own mail comes back, wearing a different address

A shared inbox on a catch-all receives every address at the domain. Combine that with forwarding anywhere in the chain and your own outbound will eventually arrive as inbound.

The obvious guard is “ignore anything whose sender is one of our own addresses”. It does not catch this, because — per the section above — the envelope has been rewritten. Ours came back with an envelope on our own sending subdomain, a VERP address that matched no user, no org address and no person, so the check saw an ordinary stranger and filed the loop as a new conversation.

Two fixes, and you want both. Recognise your sending subdomain as your own, not just your mailbox addresses. And stamp your outbound with a header of your own on the way out, so a message that comes back is identifiable regardless of what the envelope has become — the envelope is the one part of a message anything in the path is allowed to rewrite, so it is the wrong thing to build identity on.

Do not auto-reply to forwarded mail without checking

Forwarding also multiplies auto-responders. If your shared inbox sends any automatic acknowledgement, and the original sender has an out-of-office on, the two will answer each other indefinitely.

The markers exist for this: Auto-Submitted (RFC 3834) and the older, non-standard Precedence: bulk convention. Check them before any automatic reply, and mark your own automatic mail with Auto-Submitted so the other side can do the same.

De-duplicating by similarity hides the mail you most need

Forwarded mail arrives as near-duplicates by design — keep a copy and forward a copy means two copies of one message, and a shared inbox that also receives directly will see both.

So you need de-duplication. We inherited a Sørensen–Dice similarity check at a 0.9 threshold from a product where it made sense: job applicants really do re-send lightly-reworded CVs.

A shared inbox does not receive lightly-reworded CVs. It receives machine-generated transactional mail, where near-identical is normal — every one-time-passcode email is the same template with a different six-digit code. At 0.9 similarity, every passcode after the first scored as a duplicate of it and was hidden. The visible symptom was an administrator unable to log in to an unrelated product, because the codes were arriving, being stored, and never being shown.

⛔ Identity, not similarity. A duplicate is a re-delivery of the same message, which means:

Attachments deserve their own fingerprint: hash each one, sort the hashes, hash the result, so the same set in a different order still matches. And if you are tempted back toward fuzziness because two genuinely different mails collided — resist it. The failure mode of too strict is a visible duplicate someone deletes. The failure mode of too loose is silence, and nobody reports mail they never knew arrived.

Connect a domain to Yowl →