SaaS & Customer Support// diagnostic

A mail loop is manufacturing tickets between your system and theirs

In short

Group the last week's new tickets by sender domain and inter-arrival gap. A loop shows as near-identical intervals from one counterparty, often to the second — 2 autoresponders answering each other, not a spam wave. Break it at the header level first: an automatic response must never reply to mail already marked auto-generated. Then suppress the address, with an expiry date.

Key takeaways

  • Constant inter-arrival gaps from one domain is a loop. Human volume is never that regular.
  • RFC 3834 defines the Auto-Submitted header; treat any value other than 'no' as do-not-reply.
  • Never auto-respond to mail with a null return path. That rule is a MUST, not a preference.
  • Stop your own side sending before anything else. Every minute of investigation costs tickets.
  • Suppression needs an expiry date and an owner, or you silence a real customer for years.
  • Loop tickets poison label histograms and any taxonomy derived from that period's data.

Group every ticket created in the last 7 days by sender domain, then look at the gaps between arrivals within each domain. Human traffic is irregular. A loop is not: it arrives at near-identical intervals, often the same number of seconds apart every time, because the interval is the sum of 2 machines' processing latency and nothing else is involved.

That single query separates a mail loop from the things it gets mistaken for — a spam wave, a viral incident, a broken integration. It also tells you which side is generating: if the gap is roughly your own notification latency and the sender is one of your own addresses, the loop is entirely inside your estate and the fix takes one minute.

The signature, and the three things it is not

  1. Count new tickets per hour for 14 days, split by sender domain. A loop appears as a step, not a ramp: volume goes from normal to enormous between 2 consecutive hours.
  2. Within the offending domain, compute the gap between consecutive arrivals. A standard deviation near zero is conclusive. Real customers writing in bursts produce gaps that vary by minutes.
  3. Read the full headers on 3 of the tickets, not the rendered body. The body is usually a quoted chain that grows by 1 layer each turn; the headers say who generated it and whether anyone claimed it was automatic.
  4. Check whether the sender address is yours. A shared alias subscribed to its own outbound notifications produces a perfect self-loop, and it is common enough to rule out before contacting anyone.
  5. Count the Received header fields on the newest ticket against the oldest. A hop count climbing turn after turn confirms the message is circulating rather than being sent afresh.

The headers that exist precisely to stop this

Mail loops between automatic responders are an old problem with a written answer. RFC 3834 defines a header field for exactly this purpose and sets the rules both halves are supposed to follow. If your helpdesk's autoresponder ignores it, the loop was always going to happen; the counterparty just happened to be the first system careless enough to complete the circuit.

Header or propertyWhat it indicatesWhat your system must do
Auto-SubmittedDefined by RFC 3834 with the keywords no, auto-generated and auto-repliedOn receive: do not auto-respond unless the value is no. On send: mark your own automatic mail
Null return pathAn envelope sender of <> — the form used for bounces and notificationsNever auto-respond. RFC 3834 states this as a MUST, not a recommendation
List-Id and other List-* fieldsMailing-list traffic rather than a person writing to youTreat as not-a-customer. RFC 3834 permits a responder to ignore such messages outright
Precedence: bulk, junk or listWidely used, explicitly non-standard, and interpreted inconsistentlyCheck it on receive as a weak extra signal. Do not depend on it, and do not rely on it on send
Received countOne field added per relay hopUse as confirmation: a count climbing each turn means circulation, not fresh sends
In-Reply-To and ReferencesThread lineage, per RFC 5322A References chain growing by exactly 1 identifier per turn is the loop's fingerprint
What to check on receive, and what to set on send

RFC 3834 also carries the rule that matters most once a loop has started, and it is the one almost nobody implements: an automatic responder should not send the same response to the same sender more than once within a period of several days, with 7 days given as the recommended default. A responder honouring that cannot loop at volume even if every other check fails, because the second identical response never leaves.

Setting the header protects the other side. Checking the header protects you. Almost every looping system does exactly one of the two, which is why it takes two participants to make the problem.

Four ways the circuit gets completed

  1. Your responder fires on every update, not on creation. Their reply updates your ticket, your automation acknowledges the update, their system treats the acknowledgement as a new message, and the gap between arrivals equals the 2 systems' latency. This is the most common shape and the easiest to fix: scope the trigger to ticket creation.
  2. Neither side checks Auto-Submitted. Both are technically well-behaved on their own and neither will stop. The volume continues until an inbox quota, a rate limit or a person intervenes.
  3. A reopen automation triggered by out-of-office replies. The ticket closes, an absence notice arrives, the automation reopens and notifies, the absence notice fires again. Distinctive because the volume is lower and it can run for weeks unnoticed — 1 ticket reopening 40 times rather than 400 new tickets.
  4. A shared alias subscribed to its own notifications. The support address sits in a CC list, a distribution group or its own follower list, so every outbound message returns as inbound. Nothing external is involved, and it usually starts the day somebody adds the alias to a group for convenience — the kind of arrangement untangled in splitting a single support alias into lanes.

There is a fifth trigger worth naming because it comes from a well-intentioned feature: a proactive outbound broadcast. A batch notification telling customers a request finally shipped, sent to a list that contains a shared alias or 2 ticketing addresses, starts a loop from a standing start. The design that avoids it is in telling the customer their request finally shipped, and the relevant part is that outbound campaigns need the same header discipline as replies.

Breaking it now, in the right order

  1. Disable your side's response for that sender. One condition on one trigger. Do this before diagnosing anything, because every minute of investigation is more tickets and more mail leaving your domain.
  2. Apply the header rules globally, not just to this sender. Suppress automatic responses to any message carrying Auto-Submitted with a value other than no, to any message with a null return path, and to anything carrying List-* fields.
  3. Set Auto-Submitted on your own automatic mail. Acknowledgements, notifications and out-of-office equivalents all need it. This is the half that stops you being the careless participant next time.
  4. Add the per-sender rate limit. Even without headers, a rule that refuses to send the same automatic response to the same address twice within a few days makes loops self-limiting.
  5. Suppress the specific address, with an end date. Not the domain, and not forever.
  6. Only then clean up the tickets the loop already created.

Suppression needs an expiry date and a name against it

Every suppression list eventually becomes an archaeological record of incidents nobody remembers. An address suppressed during a loop in March is still suppressed 3 years later, and by then it belongs to a real person at a company that thinks you ignore their email.

  • Set a review date at creation — 30 days is a defensible default for an incident suppression, and the entry should expire rather than persist by inertia.
  • Record why, in a field, not in a ticket comment. The next person needs to know it was a loop and not abuse, because the 2 have opposite resolutions.
  • Suppress the response, not the ingestion. Keep creating or logging the inbound records so you can see whether the loop has stopped; you are muting your replies, not deafening yourself.
  • Mark the affected tickets in a structured field rather than a free-text tag, so reporting can exclude them later — the argument for that choice is in tags or custom fields for triage output.

The email you owe the counterparty

Your half of the fix stops the volume. It does not stop their system sending, and if their responder has no rate limit it may keep writing into your suppressed address indefinitely. Someone has to tell a human. The message that works is short, names the mechanism, and does not assign blame: the 2 systems were answering each other, here is the address involved, and their responder should not reply to mail marked auto-generated.

Expect this to be unfamiliar to whoever receives it. Mail loops are invisible from inside a single estate — each side sees an unusual volume of incoming mail and assumes the other is malfunctioning. Naming RFC 3834 in the message gives their engineer something concrete to act on, which is more useful than describing the symptom.

Cleaning up what the loop already wrote

The tickets are the smaller problem; the contaminated data is the larger one. A loop of several hundred near-identical tickets lands in whichever intent bucket the classifier picks, and every derived number for that period is now wrong: label distributions, volume trends, agent workload, and any taxonomy built from the affected window. If you were planning to derive a label set from recent history, exclude the period explicitly — the sampling discipline in deriving a label set from ninety days of tickets assumes the sample is real customer traffic.

  • Close in bulk with notifications suppressed. A bulk update that emails 400 confirmations restarts the loop with your own hand — the safe procedure is in re-triaging an aged queue without emailing anyone.
  • Do not merge them into 1 ticket unless the counterparty is a real customer whose original request is buried in there. Merging preserves the noise inside a thread somebody will later have to read.
  • Check what the loop did to assignment. Under round-robin, a few hundred manufactured tickets distribute evenly across every agent and each has to clear them by hand, which is one of the failure modes weighed in priority lanes or round-robin assignment.
  • Re-run any report that covered the window, with the loop tickets excluded, before anyone makes a staffing decision on the original version.

One thing this diagnosis does not cover: mail your helpdesk filed as machine-generated before any of your rules ran. That is a different mechanism with its own recovery, described in the suspended ticket queue nobody reads. Header hygiene, suppression with expiry and a clean bulk-update path are unglamorous internal tools and ops work, and they belong in the first version of any system in ticket and shared-inbox triage rather than in the incident review after the first loop — which is how we approach it in our SaaS and customer support practice.

Frequently asked questions

Short answers to the follow-ups this page tends to raise.

How do I tell a mail loop from a genuine spike in tickets?

By the regularity of the gaps, not the volume. Loop arrivals from one domain are separated by almost identical intervals because the interval is 2 machines' latency; human volume, even during an outage, varies by minutes between messages. A standard deviation near zero on inter-arrival time within a single sender domain is effectively conclusive, and it takes one query to check.

Which header actually stops auto-reply loops?

Auto-Submitted, defined in RFC 3834. Your responder must set it on outbound automatic mail and must refuse to respond to any inbound message carrying it with a value other than no. Both halves are needed and most systems implement only one, which is why loops still happen between two otherwise well-behaved platforms. The same document also forbids auto-responding to mail with a null return path.

Should we blocklist the sender to stop it?

Not as a first move, and never at domain level. Stop your own responder first, because that halves the loop immediately and cannot silence a real customer. If suppression is still needed, target the exact address, record the reason in a structured field, and give the entry an expiry date — a permanent block created during an incident is how a supplier's real mail disappears for years.

Do out-of-office replies really reopen tickets?

Yes, and it is the quietest version of this failure. A close-then-notify automation meeting an absence notice produces a ticket that reopens repeatedly for as long as the absence lasts — low volume, so it evades the alerting that catches a proper loop. The fix is to treat absence notices as automatic mail on receipt and never let them satisfy a reopen condition.

  • email loops
  • autoresponders
  • mail headers
  • ticket volume
// shipped work

The work behind this page

Builds from our portfolio that this page draws on.

Read next

Working on something in this space?

Tell us where you are in a sentence or two. We'll tell you honestly whether we're the right team, and what a sensible first slice of the work looks like.

Start the conversation