Failed email status only after three attempts

Hi @joeyk , thanks for your input, at first glance it does make sense, I will sure mention this in the process.

I still do not have the solution for the problem. And as far as I understood the core, it does not really allow plugins to inject custom logic to handle this properly.

I think if I would want that at the moment I would have to:

  • unsubscribe from EMAIL_FAILED or EMAIL_RESEND event for listener that is located in core
  • dispatch new event that extends the event that is sent to EMAIL_FAILED & EMAIL_RESEND subscribers
  • pass the response from transport callback method to mautic cli send command (which is where EMAIL_FAILED event is triggered)
  • include the status code in the event
  • and then add a subscriber to these events in plugin and handle things based on status codes.

If there is a way I can do this with existing mautic core, I would really appreciate some pointers. Otherwise I think this would make really cool PR. I might even give this a try :slight_smile:

Hi @joeyk,

I believe you misunderstood the question. The problem is how to pass the information to mautic when sending fails during submission, not after it has been submitted and SMTP responds with a specific error code.

I would also like to point out that the SMTP codes can’t be interpreted in such a simplified manner as described for multiple reasons. First of all it’s the MTA that should do the retry attempts based on correct setup. Only once the MTA determines the message as bounced (soft or hard) and passes the information to Mautic there could be an additional action like DNC. In addition this count should be applied on 3 independent send attempts and Mautic should not attempt to resend the same message - that’s the MTAs job whether that MTA is used via API or SMTP. Second problem lies with the fact that the SMTP codes reliability is quite limited - the numbers are indicative but not reliable. For example the most common case being full mailbox. Some servers will respond with 45x error to tell the sending MTA to retry delivery later (hoping that some space will free up), others respond with 55x to indicate that the sending MTA should not retry sending. To make things even worse the 45x errors with “over quota” message sometime mean that the sending MTA exhausted it’s delivery rate quota and should slow down… as I’ve been in deliverability for over 15 years I have seen all sorts of crazy SMTP codes and messages and our current MTA config has about 300 special rules on how to treat various responses.

The problem @mzagmajster is dealing with relates to API submission errors rather than SMTP responses. In this case the plugin makes an API call to send message and API responds with 401 or 404 error which needs to be interpreted as EMAIL_FAILED rather than BOUNCED as it has never been sent in a first place.

Even if we considered SMTP submission the DNC logic seems off as it would disable recipients in case the SMTP rejects the message for lack of authorization (eg. sending address not being permited to relay). The DNC should only be applied to the actual result of delivery attempt not to submission attempt.

1 Like

Yes, you are right, I share your view on everything including SMTP error codes.
Yes, I misunderstood the question. Since I’m using Amazon SES and POSTAL, luckly never have to deal with failed API attempts.
Looking forward if any improvement will be done here. Could be similar like with webhooks: mautic already has a feature where an email is sent when the webhook fails.

we are trying to catch the case where user enters invalid credentials or tries to send using a domain that is not authenticated… some of these cases would definitelly would result in failed API requests no matter what service is being used.

Hello all,

I use Mailjet and I have configured the webhook URL to have a return in Mautic in case of a problem with the recipient’s email address.
I followed this documentation: Bounce management | Mautic

I know it is possible to do the same thing with other routers.
Does this answer your question?


Hi Pierre,

this is not about bounces but about rejects on submission. Bounces are working just fine it’s handling certain edge cases during submission (like misconfiguration or domain abuse attempts) we are trying to solve.

Hello @pangea,

Thank you for the clarification.
Do we need to go into this level of detail?
For 99% of companies we don’t need to go into this level of complexity.

What do you think?

very much so. it’s not a matter of complexity but user-experience:
a) if you misconfigure your sending by mistake and send your campaign to a large list and it all fails and all contacts turn DNC you will have a headache
b) if a sender sets up the sending incorrectly and the campaign is sent using his email address as “From” and that address is not authorized he will be in trouble.
c) I don’t even want to go into the details of how this affects email security and opens the doors to email abuse.

So yes - having a distinction between submission failure and delivery failure is important.

@pangea I understand your arguments but I don’t agree.

For me it’s a matter to be seen in the Mautic interface beforehand.
It’s difficult to send a campaign by mistake, you have to validate 3 times in a row that you want to send an e-mail campaign :wink:

If the person who sets up the campaign makes a mistake, he will learn and will not do it again in the future.
It remains a software that requires a hand in hand and to know what we do.
The aim of the software is to accompany the use. :slight_smile:


The fact that the user can see the problem doesn’t mean he will understand the problem.

As an example - right now you set up your campaign and you decide you want to send it using a different From address than the one that is authorized, eg. instead of you change it to While the SMTP/API service has the setup the is not and hence the emails will either fail sending or will fail to be properly authenticated (DKIM/SPF/DMARC). In the first case it will result in DNCs, in second it will result in major reputation impact and bad deliverability. I would love if we could provide a warning in the UI before the send but I’m not sure there is such an option.

For my part, with Mailjet, if the e-mail cannot be routed because the domain is not configured, then Maimjet will block the sending and that’s all.
There will be no DNC on the Mautic side. :slight_smile:


And thats exactly what we want to figure out for the plugin. That if the message is blocked on submission it wont trigger the DNC.

If you send your emails with Mailjet, you don’t need a plugin.
Is it the implementation that needs to be reviewed for other SMTP relays or Mailjet that does not return the same responses?


We are not interested in Mailjet as we are an ESP building an integration for Mautic

So you are a competitor of Mailjet?

Thanks for the input folks, we seem to be getting a bit off topic so to summarise:

I think the fundamental issue is that if there is some problem with the API connection completely unrelated to the inbox - for example the email never left the MTA because the API limit was hit - it should not be marked as bounced or have a DNC set because the email failed to send, it didn’t bounce.

If your API limit is exceeded and you sent to 2 mil contacts they would all get marked DNC as a result, whereas it should really be an error which does not impact the bounce/deliverability.

I believe the problem is that we do not differentiate between ‘the MTA could not send this email’ and ‘the mail did not make it from the MTA to the user successfully’.

I have asked some of the developers who have been pretty deep in this area of Mautic to take a look and share their thoughts.

1 Like

Hi @rcheesley,

rate limits are perfect example - if a sending domain has a rate limit and that limit is exceeded the attempt to send has to be retried but after certain number of attempts it needs to be failed but not set as DNC. I thought of a workaround yesterday that I told @mzagmajster that if the swiftmailer core interprets the status codes as SMTP code even if they come from API, then he could return a “fake” code for certain API responses, eg. 550 for API authentication failure instead of passing back 429.

1 Like

Hi @pangea, @rcheesley

disclosure: I am not an email expert by any means, but I am working with mautic code base for quite some time.

But per my last investigation of this issue (and suggestion in the earlier post). I do not think its possible to enforce respect for api status codes during the webhook response processing (processCallback method to the extent that is required in this situation) because of some code in the core.

There is an event listener that essentially says “if failed retires is less then three try again else mark as DNC”. Until we have this peace of code in mautic core no mater what you do on plugin side or swiftmailer its not gonna bring the desired results.

I think that the most accurate representation of what I think needs to be revised in order to reach the requirement is in my earlier post.

1 Like

Thanks, @rcheesley for tagging me on this topic.

I will describe how things work and what kind of changes we bring to M5, I work a lot on the email bundle and I can support on this issue.

The current implementation of M4, has two ways to handle email sending:

  1. Immediate: where the calls are happening to the transport that you want to send through directly, and the errors are handled by that transport. For example, if you use SendGrid API in the immediate mode, if the API authentication fails, the send will not be counted, and it will not be retried, nor the contact will be marked as DNC.
    mautic/SendGridWrapper.php at 9cb69551fbff6046a1e1476aa23d2219e6e41c36 · mautic/mautic · GitHub

This sending mode has advantages of handling large batch sizes in a single API call, and direct API calls to the MTA.

It is disadvantage,that it consume lots of memory from PHP process, it timeouts, and may result in sending duplicates.

The queue mode: it store the emails as files in the var/spool folder, each email is saved as a file and processes each file in a single run. This mode is the source of all the issues

How it works?

When you select the queue mode, the queue transport is selected which basically takes the email message and converts it into a file that is stored on the disk, regardless of the status of the MAT. therefore Mautic marks all the emails as sent when you use the queue mode because it set the files on disk for the cron job to process them later. It fails in one case and emails are not marked as sent when the disk is full, has writing permissions issues, etc.

The issue of DNC, happens here

This command connects to the real transport (MTA), which tries to send, so if the API request fails for some reason like authentication error, rate limit issues, etc. the command will mark the file as .tryagain and retry up to 3 times before it marks the contact as DNC, even if the email was not sent.

Of course this implementation is not logical and DNCs should happen using webhooks and callbacks by MTA.

The good news is that we will be moving away form swiftmailer to a new mailing system that uses symfony/mailer/ and symfony/messeneger. you can see the PR here

Next Tuesday we will have a call about the new changes in the mailing systems, so anyone who likes to join please message me on slack with your email and I will add you.

I hope this covers the issue and help you better understand how EmailBundle works


Hi folks!

We have a PR to mark emails as failed if they fail, and not set DNC:

We would value testing and your thoughts!

Here’s how to test:

1 Like