Skip to content

Docs / Config

PowerMTA bounce & feedback-loop (FBL) handling

Updated 2026-06-05· 7 min read· bring-your-own-license

PowerMTA logs delivery events to its accounting file as d (delivery), b (bounce) and rb (rebound) records with DSN status, diagnostic text and a bounce category. You suppress hard (5xx) bounces immediately, let soft (4xx) bounces retry then expire via bounce-after, and register your IPs/domains for provider feedback loops so ARF complaints reach an address you process. The goal is simple: stop mailing dead addresses and people who complained.

Sending is only half of deliverability; the other half is listening to what comes back. PowerMTA records every delivery, bounce and complaint — and what you do with those records decides whether your reputation holds. This page covers the accounting file, the bounce classification PowerMTA gives you, hard vs soft handling, and feedback loops — and how all of it feeds one suppression list.

The accounting file

PowerMTA writes structured records of every event to an <acct-file>. The three core types are d (delivery), b (bounce) and rb (rebound — a transient failure). Add f for feedback (complaint) events. Log the fields a bounce handler needs:

<acct-file /var/log/pmta/acct.csv>
    records d,b,rb,f
    record-fields d orig,rcpt,dsnStatus,vmta,jobId,timeLogged
    record-fields b orig,rcpt,dsnStatus,dsnDiag,bounceCat,vmta,jobId
    move-interval 1h
</acct-file>

bounceCat is PowerMTA's own classification (bad-mailbox, bad-domain, spam-related, policy-related, and so on) — it's the most useful single field for deciding whether to suppress an address, because it abstracts away the wording differences between receivers.

A few record details matter. move-to relocates rotated files to a directory your handler watches, move-interval sets how often, and world-readable yes lets a separate process read them. Many setups split the streams — a bounces.csv with records b, rb and a separate fbl.csv with records feedback-loop — so the bounce handler and the complaint handler each read their own file. The fields you log per record type are yours to choose; include at minimum the recipient, the DSN status, and bounceCat.

The bounceCat classification

The reason bounceCat is so useful deserves spelling out. Receivers return failure reasons as free text — Gmail phrases it one way, Outlook another, a regional ISP a third — and writing suppression rules against that raw text is fragile. PowerMTA parses each response and assigns a stable category (an integer with a name like bad-mailbox) plus a type (Hard, Soft, Block, Admin or Undetermined). Your code keys on the category and type, not the wording, and there are roughly twenty categories that have stayed stable across PowerMTA versions.

Crucially, not every bounce category means “suppress the subscriber.” They fall into three groups:

GroupExample categoriesAction
Recipient is badbad-mailbox, bad-domain, inactive-mailboxSuppress the address — hard
Temporaryquota-issues, bad-connection, content-relatedRetry, then expire — soft
Sender / reputationspam-related, policy-related, routing-errorsLog for stats; usually don’t suppress the subscriber

That third group is the one people get wrong: a policy-related or spam-related block is telling you about your sending or reputation, not about a dead recipient — suppressing the subscriber hides a problem you should be fixing. Route those to your monitoring, not your suppression list.

Hard vs soft bounces

A hard bounce is a permanent 5xx failure — the mailbox or domain doesn't exist. Suppress it immediately. A soft bounce is a temporary 4xx — a full mailbox, a greylist, a throttle — that should be retried before you give up. Control retry behaviour per domain:

<domain *>
    retry-after 10m
    max-retries 30
    bounce-after 4d12h     # stop retrying soft failures after this
</domain>

Tune these per provider when you have the volume to justify it; the defaults are sane to start. The important rule is that hard bounces never get retried and never get mailed again.

Soft bounces need a policy too, since “temporary” can become “never”. A mailbox that’s been full for weeks, or an address that soft-bounces on every send, is effectively dead even though each individual failure is a 4xx. The common rule is to suppress an address after it soft-bounces a set number of times in a row (five is typical), or after it’s gone unresponsive for around ninety days. PowerMTA’s bounce-after handles giving up on a single message; the repeated-soft-bounce rule is something your handler or sending app applies across messages. The principle is the same as hard bounces, just on a delay: stop spending sends and reputation on addresses that never accept mail.

Feedback loops (FBL)

Complaints don't come back as bounces — a recipient hitting "this is spam" is invisible unless you're enrolled in the provider's feedback loop. These programs forward you a copy of each complaint in ARF format. Register your sending IPs and domains with each one:

  • Yahoo / the broader Complaint Feedback Loop program
  • Microsoft JMRP (Junk Mail Reporting) and SNDS (Smart Network Data Services)
  • Other regional providers that offer an FBL or complaint feed

Point the ARF reports at an address you actually process, parse them, and suppress every complainer at once. Note that Gmail doesn't offer a per-message FBL — you watch the complaint rate in Postmaster Tools instead, which is why keeping it under 0.3% matters.

Wire it into your sending app

The accounting file is the bridge to your application. MailWizz's PowerMTA delivery server reads it natively to update stats and suppress addresses; with a plain SMTP setup you pipe the acct-file to a bounce handler that reports back. Either way the non-negotiable outcome is that hard bounces and complainers leave your list. Track the result with the deliverability calculator and decode unfamiliar failures with the SMTP code lookup.

VERP: knowing which message bounced

A bounce is only actionable if you know which recipient and send it belongs to, and that’s what VERP — Variable Envelope Return Path — provides. Instead of a single shared Return-Path, you encode the recipient (and often the campaign or message ID) into the envelope return address, so when a bounce comes back, the address it was sent to tells you exactly who failed without parsing the bounce body.

This matters because bounce bodies are inconsistent and sometimes strip the original recipient, whereas the envelope return path is reliably preserved. PowerMTA records the original recipient in the accounting log regardless, but VERP-style return paths make app-side correlation trivial and survive cases where the remote server’s bounce is malformed. For feedback-loop complaints the equivalent trick is an X-FBLId header you add on the way out, which the ARF report echoes back — letting you map a complaint to the exact recipient and campaign that triggered it.

FBL processing in PowerMTA

PowerMTA can ingest the ARF complaint reports itself through a dedicated accounting record. A feedback-loop record (type f) captures the complaint with fields like the reported domain, the format, and the X-FBLId you stamped on the original message:

<acct-file /var/log/pmta/fbl.csv>
    records feedback-loop
    record-fields f timeLogged,format,reportedDomain,header_X-FBLId
    move-interval 24h
    move-to /var/log/pmta/accounting/fbl/
</acct-file>

The ARF format classifies each report by type — abuse (the spam-button case you act on), fraud, virus, other and not-spam. Your handler reads the X-FBLId to identify the recipient, confirms the type is a real complaint, and suppresses that address. The conceptual background — which providers offer FBLs, how ARF is structured, and why Gmail is the exception — is covered in what is a feedback loop; this is the config side of wiring it up.

Feeding MailWizz, Interspire or a database

The accounting file is the hand-off point to whatever owns your suppression. MailWizz reads the PowerMTA accounting file natively through its delivery-server integration, updating stats and suppressing addresses without a separate script. Interspire and similar platforms use a bounce-processing addon that parses the same log and disables subscribers — typically after one hard bounce or several soft ones.

With a plain SMTP setup and no off-the-shelf reader, you pipe the rotated accounting files to a small handler that parses each record, applies your suppression rules by bounceCat and type, and writes to a central suppression database. Larger operations also ship the logs into analytics — ELK, ClickHouse, BigQuery — to build per-ISP, per-campaign and per-pool dashboards. Whatever the destination, the contract is the same: the accounting file is the source of truth, and a hard bounce or complaint in it must end as a suppression everywhere.

One suppression list, every signal

The mental model that keeps this manageable: every negative signal flows into a single suppression list. Hard bounces, complaints from feedback loops, and unsubscribe requests are different events, but they share one destination — an address that lands on that list is never mailed again, by any stream. Treating bounces and complaints as separate systems with separate lists is how addresses slip back into a send they should have been excluded from.

Make the list append-only and authoritative: nothing re-adds a suppressed address, no “win-back” import quietly reintroduces complainers, and every send — transactional, marketing, every domain and pool — checks against it. That single, global, never-shrinking list is the practical core of bounce and FBL handling; the accounting records, categories and ARF reports all exist to feed it accurately. Get the list right and the rest is plumbing.

Monitoring the rates

Processing bounces and complaints is half the job; watching the resulting rates is the other half, because the rates are your early warning. Keep bounces under about 2% and spam complaints well under 0.3% — Google’s official line is around 0.1%, and they flag a per-day spike toward 0.3% as a problem. A bounce rate climbing past 2% usually means a stale or poorly-sourced list; a complaint rate creeping up means a consent, frequency or content problem.

Watch them where the providers show them: your own accounting-derived numbers for bounces, and Postmaster Tools (Gmail) and SNDS (Microsoft) for complaint and reputation signals, including the Gmail complaints no per-message FBL will ever name. When a rate crosses the line, the response is to pause and fix the cause — tighten targeting, slow frequency, clean the list — not to push through it. Bounce and complaint rates are the dials that tell you whether your suppression and your list hygiene are actually working; rising numbers mean they aren’t.

KumoMTA bounce and complaint handling

KumoMTA approaches the same job through its logging and Lua hooks. It classifies delivery responses and emits structured log events for deliveries, bounces and deferrals that you consume much as you would PowerMTA’s accounting records, and complaint (ARF) handling is wired up in Lua rather than through an <acct-file> directive. The categories, the hard-versus-soft logic, and the suppression outcome are identical — only the mechanism for getting the events out differs.

So the design carries over unchanged: classify the failure, suppress hard bounces and complainers into one global list, retry and expire soft bounces, and route reputation-related blocks to monitoring rather than suppression. Whichever engine you run, the accounting-or-log feed into a single authoritative suppression list is the part that matters, and the Auto PMTA Configurator wires it up for either.

Common bounce and FBL mistakes

The damaging errors are consistent. Not processing at all — sending without reading what comes back — lets dead addresses and complainers pile up until reputation collapses. Suppressing reputation-related bounces as if they were bad recipients hides a sending problem behind a shrinking list. Retrying hard bounces wastes sends and signals carelessness to providers.

The subtler ones: per-campaign instead of global suppression, so a complainer still gets your other streams; delayed processing that re-mails someone between their complaint and your next batch run; and re-adding suppressed addresses through a later import. Each is avoided by the same discipline — process promptly, suppress globally and permanently, and never let anything re-add an address the feedback told you to drop.

A complete accounting setup

Putting the pieces together, a working configuration logs bounces and feedback separately, with the fields a handler needs and rotation into directories it watches:

# bounces + rebounds for the bounce handler
<acct-file /var/log/pmta/bounces.csv>
    records b, rb
    record-fields b timeLogged,orig,rcpt,bounceCat,dsnStatus,dsnDiag,vmta,header_X-FBLId
    move-interval 1h
    move-to /var/log/pmta/accounting/bounces/
    world-readable yes
</acct-file>

# complaints for the FBL handler
<acct-file /var/log/pmta/fbl.csv>
    records feedback-loop
    record-fields f timeLogged,format,reportedDomain,header_X-FBLId
    move-interval 24h
    move-to /var/log/pmta/accounting/fbl/
</acct-file>

A handler then watches each move-to directory: the bounce process suppresses on hard bounceCat values and counts repeated soft ones, while the FBL process suppresses on every genuine complaint, both writing to the same suppression list. Rotate often enough (hourly for bounces) that suppression is near-real-time rather than a daily batch, since the gap between a complaint and the next send is where damage happens.

The half that’s easy to skip

It’s worth naming why this gets neglected: bounce and FBL handling is invisible when it works and only noticed when reputation has already slipped. Setting up sending feels like progress; setting up the feedback path feels like overhead — right up until accumulated dead addresses and unsuppressed complainers drag your placement down, at which point it’s a recovery project rather than a config step. The senders with durable deliverability are the ones who treated the return path as half the system from day one.

The payoff is concrete: a sender that suppresses fast keeps bounce and complaint rates low, which keeps reputation high, which keeps mail in the inbox — a virtuous loop that runs itself once the pipeline exists. Skipping it doesn’t save work; it defers the work to a worse time, after the damage, when you’re cleaning a list and rebuilding reputation instead of quietly maintaining both. Build the listening half alongside the sending half, not after it.

The bottom line

Bounce and FBL handling is how a sender listens. PowerMTA writes every delivery, bounce and complaint to its accounting file with a stable bounceCat category and type; you suppress hard bounces (bad mailbox/domain) immediately, retry soft bounces and expire the persistent ones, route reputation-related blocks to monitoring, and process feedback-loop ARF reports to suppress complainers. Every one of those signals lands in a single, global, append-only suppression list that every send checks.

Use VERP and an X-FBLId to map failures back to the exact recipient, feed the accounting file into MailWizz, Interspire or your own database, and watch bounce (under ~2%) and complaint (under ~0.1–0.3%) rates as your early warning. KumoMTA does the same through logs and Lua. If you’d rather not build the classification and suppression pipeline yourself, our managed deliverability service wires it up, and the Auto PMTA Configurator sets up accounting, bounce processing and FBL handling at deploy. None of it is glamorous, and that’s rather the point: it’s the quiet, automatic plumbing that keeps a clean list clean, so the reputation you warm up so carefully isn’t slowly eroded by mail to addresses that no longer exist or people who already told you to stop. Build it once, wire it to a single suppression list, and it protects every send after, across every stream and every IP, with no further thought required beyond watching the rates stay low and acting the moment they don’t, which is exactly the low-effort, high-payoff maintenance that durable sending reputations are quietly built on, send after send, long after the initial setup has become a distant memory and the pipeline simply runs on its own in the background, removing the dead and the unwilling from your list before they ever cost you an inbox — for legitimate, opt-in sending.

Frequently asked questions

How does PowerMTA record bounces? +

PowerMTA writes delivery events to its accounting file. The record types are d (successful delivery), b (bounce / permanent failure) and rb (rebound, a transient failure that may retry). Each bounce record can include the recipient, the DSN status code, the remote server's diagnostic text (dsnDiag) and PowerMTA's bounce category (bounceCat), which is what a bounce handler reads to decide whether to suppress an address.

What's the difference between a hard and a soft bounce? +

A hard bounce is a permanent failure (5xx) — the mailbox or domain doesn't exist — and the address should be suppressed immediately and never mailed again. A soft bounce is temporary (4xx) — a full mailbox, a greylist, a momentary throttle — and PowerMTA retries it on a schedule before giving up. In PowerMTA you control this with retry-after, max-retries and bounce-after.

What is a feedback loop (FBL)? +

A feedback loop is a program run by a mailbox provider that forwards you a copy, in ARF format, every time one of your recipients clicks 'this is spam'. You register your IPs/domains with each provider (Yahoo/Verizon FBL, Microsoft's JMRP/SNDS, and others), receive the complaints at an address you designate, and suppress those complainers. It's the only reliable way to see complaints, since they don't come back as bounces.

What complaint and bounce rates are acceptable? +

Keep spam complaints under 0.3% (ideally under 0.1%) and bounces under about 2%. Above those, providers throttle or block you. The fix is never to hide the numbers — it's list hygiene: honest opt-in, prompt suppression of hard bounces and complainers, and removing unengaged recipients.

Can I just ignore bounces and complaints? +

No. Repeatedly mailing dead addresses and people who complained is exactly what destroys sender reputation and lands the rest of your mail in spam. Processing bounces and FBLs isn't optional housekeeping — it's the core of staying deliverable.

Related