Sending, Blocking & Mail Logs
The package listens to Laravel’s MessageSending and MessageSent events. Every outgoing mail is validated and logged, whatever mailer it goes through.
What happens on send
For every To, Cc and Bcc recipient:
- The address is validated: format, then an MX record that resolves to an IP. A previous verdict from the database is reused while it is fresh. Set
MAILTRAP_VALIDATION_ENABLED=falseto skip this; the DNS lookups run synchronously during the send. - A
blockedaddress aborts the whole send with aSymfony\Component\Mailer\Exception\TransportException, after aMailBlockedevent. SetMAILTRAP_BLOCK_INVALID_EMAILS=falseto send anyway and only log the reason. - Otherwise a
MailLogrow is created. It is marked sent onceMessageSentfires.
Validation states
| Status | Set by | Blocks sending? |
|---|---|---|
valid | Passed the checks, or a delivery/open/click webhook event | No |
invalid | A bounce, spam or reject webhook event | No |
blocked | A failed local check, or markAsBlocked() | Yes |
Blocking is per address: a typo or a manual block never stops mail to the rest of the domain. Blocks from a local check expire after MAILTRAP_VALIDATION_CACHE_DURATION, so a temporary DNS outage does not block an address for good. Manual blocks are permanent.
use Darvis\Mailtrap\Models\EmailValidation;
$error = EmailValidation::validateEmail('test@example.com'); // null when valid
// Stop all mail to one address.
EmailValidation::markAsBlocked('complainer@example.com', 'Spam complaint');
More in the EmailValidation documentation.
Deprecated:
MailtrapServiceandapp('mailtrap')call a Mailtrap validation endpoint that does not exist, sovalidateEmail()there cannot succeed. They are removed in 2.0. UseEmailValidation::validateEmail()instead.
Linking a log to a model
Tag a Mailable with headers:
public function headers(): \Illuminate\Mail\Mailables\Headers
{
return new \Illuminate\Mail\Mailables\Headers(text: [
'X-Mail-Type' => 'invoice',
'X-Mail-Model' => Invoice::class,
'X-Mail-Model-ID' => (string) $this->invoice->id,
]);
}
Then query the logs:
use Darvis\Mailtrap\Models\MailLog;
MailLog::forModel(Invoice::class, $invoice->id)->failed()->get();
MailLog::pending()->count(); // also: successful(), blocked()
$log->related; // the Invoice
Cleaning up old logs
Logs older than MAILTRAP_CLEANUP_AFTER_DAYS are removed by Laravel’s pruning. Package models are not discovered automatically, so schedule it explicitly:
// routes/console.php
Schedule::command('model:prune', ['--model' => [\Darvis\Mailtrap\Models\MailLog::class]])->daily();
Events
| Event | When | Properties |
|---|---|---|
Darvis\Mailtrap\Events\MailBlocked | Just before a send to a blocked address is aborted | email, reason, message, mailLog |
Darvis\Mailtrap\Events\MailtrapEventReceived | For every signed webhook event, also the ones the package ignores | type, email, payload, mailLog |
use Darvis\Mailtrap\Events\MailtrapEventReceived;
use Illuminate\Support\Facades\Event;
Event::listen(function (MailtrapEventReceived $event): void {
if ($event->type === 'unsubscribe') {
User::where('email', $event->email)->update(['newsletter' => false]);
}
});
A listener that throws is logged (with MAILTRAP_LOG_TO_LARAVEL=true) and does not stop the rest of the webhook batch.
Other mailers
Validation and logging work with any transport: Mailtrap SMTP, Microsoft Graph, Amazon SES or any other Laravel mailer. Delivery feedback is the exception, because only Mailtrap reports it through the webhook.
| Capability | Any mailer | Mailtrap only |
|---|---|---|
| Pre-send validation (format / MX / blocklist) | ✅ | |
| Outgoing mail logging | ✅ | |
Inbox UI & mailtrap:test health check | ✅ | |
Delivery / open / click → markAsValid | ✅ | |
Bounce / spam / reject → markAsInvalid | ✅ |
So you can route production mail through Microsoft Graph and still get full logging and the inbox. Only the feedback loop (confirming good addresses, flagging bounces) needs Mailtrap.