Knowledge base

A return address that explains itself

Who, what and when — right in the envelope address

The task. A bounce or complaint arrives several days after sending. Figuring out which client, campaign and recipient it concerns means either parsing a report whose format differs for every receiver, or reading the address it arrived at.

What it is made of:

  • <bounce-processor> — identifies a bounce report and writes an rb record;
  • verp — a unique return address for each recipient;
  • <feedback-loop-processor> — the same for complaints, by its own address list;
  • job-header — the campaign label in accounting records.

The parts have different scopes. The processors are node-level, and their lists are matched against the address the message arrived at; changing the bounce processor’s list requires a restart. Both require the node to accept this mail: for complaints, to be the MX of the complaint domain.

How they interact. The manual recommends a unique address per message directly — something like customer1-campaign563-uniqueID498294@bounce.yourdomain.com — and the idea generalizes: such an address names the campaign and the recipient by itself, and identification becomes a lookup rather than an interpretation. VERP generates such addresses itself, so the two halves of the scheme are one solution from different ends: at submission and at receipt. The scheme does not eliminate the variety of formats: identification is reliable, parsing is not, so unidentified reports are worth keeping.

The main trap is overlapping processor lists. The bounce processor runs before the complaint processor: an address matching both lists is taken by the first, and complaints silently become “bounces”.
What the Hub does. It applies half of it. The processors on the node are in place: bounces are received at bounce@bounce.<domain> (return address rewriting is turned on with a checkbox in the generator), complaints at fbl@fbl.<domain>, and the parsed records go to the Hub by webhook. But there is one return address per domain: VERP is not enabled, and the Hub does not carry the mailing tag — process-x-job false. It takes the recipient from the accounting record field, not from the address.
Recommended values

What each part gives:

Part Where it is set What appears
bounce address list <bounce-processor> rb records
VERP message submission a unique return address per recipient
complaint address list <feedback-loop-processor> f records
job the x-job header and process-x-job the jobId field
The processor order, the restart on a list change, the advice about a unique address and VERP come from the manual (§ 13.6, § 13.7, § 7.4.4, § 13.8). The scheme itself is our hypothesis.
Example configuration
<bounce-processor>
    <address-list>
        address /^b-[a-z0-9-]+@bounce\.example\.com$/    </address-list>
</bounce-processor>
<feedback-loop-processor>
    <address-list>
        address /^fbl@fbl\.example\.com$/
    </address-list>
</feedback-loop-processor>
The lists do not overlap: bounce. and fbl. are different subdomains; after changing the bounce list, a restart is needed.
yellow — — directive names, blue — blocks. They are highlighted the same way in the text of the card.
Compatibility
PowerMTA 6.0
described in the manual of this version · platform: any
The vendor does not state the first version for every directive — there is no note here. The name also appears in the 5.0 edition of the manual.
How to check on the node
Check that reports are identified:
grep -c '^rb,' /var/log/pmta/acct-*.csv
Zero while reports are arriving — the address did not match the list or the rb type is not selected in records.