---
title: "Events"
manual: "Mail system"
version: "4.3"
permalink: "https://docs.typo3.org/permalink/mediaessenz/mail:events@4.3"
source: "Reference/Events/Index.rst"
rendered: "2026-11-05T22:41:23+00:00"
---

# Events {#events}

EXT:mail dispatches PSR-14 events at the places where an integrator may want to change its behavior. All events are
final classes in the namespace `MEDIAESSENZ\Mail\Events`.

| Event | Dispatched |
| --- | --- |
| [AdditionalMailHeadersEvent](https://docs.typo3.org/permalink/mediaessenz/mail:events-additional-mail-headers@4.3) | Before each single mail is sent. Adds or changes mail headers. |
| [DeactivateRecipientsEvent](https://docs.typo3.org/permalink/mediaessenz/mail:events-deactivate-recipients@4.3) | When returned recipients are deactivated in the report module. |
| [ManipulateCsvImportDataEvent](https://docs.typo3.org/permalink/mediaessenz/mail:events-manipulate-csv-import-data@4.3) | Before the records of a csv import are written to the database. |
| [ManipulateImportFolderEvent](https://docs.typo3.org/permalink/mediaessenz/mail:events-manipulate-import-folder@4.3) | When the folder for the uploaded csv files is determined. |
| [ManipulateImportStorageFolderEvent](https://docs.typo3.org/permalink/mediaessenz/mail:events-manipulate-import-storage-folder@4.3) | When the folders are collected, which can store imported records. |
| [ManipulateMailRecipientsEvent](https://docs.typo3.org/permalink/mediaessenz/mail:events-manipulate-mail-recipients@4.3) | When the recipients of a mailing are determined. |
| [ManipulateMarkersEvent](https://docs.typo3.org/permalink/mediaessenz/mail:events-manipulate-markers@4.3) | When the markers of a recipient are replaced in the mail content. |
| [ManipulateRecipientEvent](https://docs.typo3.org/permalink/mediaessenz/mail:events-manipulate-recipient@4.3) | Whenever the data of a recipient is loaded. |
| [RecipientBouncedEvent](https://docs.typo3.org/permalink/mediaessenz/mail:events-recipient-bounced@4.3) | After a returned mail was analysed and stored by the bounce mail command. |
| [RecipientEmailCorrectedEvent](https://docs.typo3.org/permalink/mediaessenz/mail:events-recipient-email-corrected@4.3) | After the address of a failed delivery was corrected in the queue module. |
| [RecipientsRestrictionEvent](https://docs.typo3.org/permalink/mediaessenz/mail:events-recipients-restriction@4.3) | When the query for recipients of an extbase model source is built. |
| [ScheduledSendBegunEvent](https://docs.typo3.org/permalink/mediaessenz/mail:events-scheduled-send-begun@4.3) | When the delivery of a mailing begins. |
| [ScheduledSendFinishedEvent](https://docs.typo3.org/permalink/mediaessenz/mail:events-scheduled-send-finished@4.3) | When the delivery of a mailing is finished. |

-   [Registering a listener](https://docs.typo3.org/permalink/mediaessenz/mail:registering-a-listener@4.3)
-   [The recipient source configuration](https://docs.typo3.org/permalink/mediaessenz/mail:the-recipient-source-configuration@4.3)
-   [AdditionalMailHeadersEvent](https://docs.typo3.org/permalink/mediaessenz/mail:additionalmailheadersevent@4.3)
-   [DeactivateRecipientsEvent](https://docs.typo3.org/permalink/mediaessenz/mail:deactivaterecipientsevent@4.3)
-   [ManipulateCsvImportDataEvent](https://docs.typo3.org/permalink/mediaessenz/mail:manipulatecsvimportdataevent@4.3)
-   [ManipulateImportFolderEvent](https://docs.typo3.org/permalink/mediaessenz/mail:manipulateimportfolderevent@4.3)
-   [ManipulateImportStorageFolderEvent](https://docs.typo3.org/permalink/mediaessenz/mail:manipulateimportstoragefolderevent@4.3)
-   [ManipulateMailRecipientsEvent](https://docs.typo3.org/permalink/mediaessenz/mail:manipulatemailrecipientsevent@4.3)
-   [ManipulateMarkersEvent](https://docs.typo3.org/permalink/mediaessenz/mail:manipulatemarkersevent@4.3)
-   [ManipulateRecipientEvent](https://docs.typo3.org/permalink/mediaessenz/mail:manipulaterecipientevent@4.3)
-   [RecipientBouncedEvent](https://docs.typo3.org/permalink/mediaessenz/mail:recipientbouncedevent@4.3)
-   [RecipientEmailCorrectedEvent](https://docs.typo3.org/permalink/mediaessenz/mail:recipientemailcorrectedevent@4.3)
-   [RecipientsRestrictionEvent](https://docs.typo3.org/permalink/mediaessenz/mail:recipientsrestrictionevent@4.3)
-   [ScheduledSendBegunEvent](https://docs.typo3.org/permalink/mediaessenz/mail:scheduledsendbegunevent@4.3)
-   [ScheduledSendFinishedEvent](https://docs.typo3.org/permalink/mediaessenz/mail:scheduledsendfinishedevent@4.3)

## Registering a listener {#events-register-listener}

A listener is a class with an `__invoke()` method, whose argument is the event. The simplest way to register it is
the PHP attribute `AsEventListener`. This is also how the listeners of EXT:mail itself are registered.

**EXT:my_extension/Classes/EventListener/MyListener.php**

```php
<?php

declare(strict_types=1);

namespace Vendor\ExtensionKey\EventListener;

use MEDIAESSENZ\Mail\Events\ManipulateMarkersEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;

#[AsEventListener(identifier: 'vendor/extensionkey/my-listener')]
final class MyListener
{
    public function __invoke(ManipulateMarkersEvent $event): void
    {
        // change the event here
    }
}
```

Alternatively, register the listener in the [`Configuration/Services.yaml`](https://docs.typo3.org/m/typo3/reference-coreapi/12.4/en-us/ExtensionArchitecture/FileStructure/Configuration/ServicesYaml.html#file-extension-configuration-services-yaml) of your extension:

**EXT:my_extension/Configuration/Services.yaml**

```yaml
Vendor\ExtensionKey\EventListener\MyListener:
  tags:
    - name: 'event.listener'
      identifier: 'vendor/extensionkey/my-listener'
```

If several listeners change the same event, you can set their order with the arguments `before` and `after` of the
attribute (or the tag). The identifiers of the listeners of EXT:mail are the file names in kebab case with the prefix
`mediaessenz/mail-`, for example `mediaessenz/mail-normalize-recipient-data`.

## The recipient source configuration {#events-recipient-source-configuration}

Several events carry a `MEDIAESSENZ\Mail\Domain\Model\Dto\RecipientSourceConfigurationDTO`. It describes
the recipient source of a recipient, as it is configured in the recipient sources of your site. The most important
public properties are:

-   **`identifier`**

    The identifier of the source, for example `tt_address` or `fe_users`. Csv groups have the identifier
    `tx_mail_domain_model_group:<uid of the group>`.

-   **`title`**

    The title of the source.

-   **`table` and `model`**

    The database table, or the Extbase model class of the source.

-   **`ignoreMailActive`**

    If checked, recipients are used even if they are not marked as active for mailings.

-   **`forceHtmlMail`**

    If checked, HTML mails are sent to all recipients of the source, no matter what they chose.

-   **`custom`**

    An array for own settings of the source.

The type of the source can be asked with the methods `isTableSource()`, `isModelSource()`, `isCsv()`,
`isPlain()` and `isCsvOrPlain()`. Use them if your listener should only handle some of the sources.

## AdditionalMailHeadersEvent {#events-additional-mail-headers}

Dispatched for every single mail, right before it is sent. It adds the possibility to add or change mail headers.

-   **`getHeaders()`**

    The `Symfony\Component\Mime\Header\Headers` of the message. Change this object. The event has no setter.

-   **`getMail()`**

    The mailing (`MEDIAESSENZ\Mail\Domain\Model\Mail`), which the message belongs to.

-   **`getTypo3MailId()`**

    The value of the header `X-TYPO3MID`. It identifies the mailing, the recipient source and the recipient and is used by
    the bounce analysis.

-   **`getRecipientSourceIdentifier()` and `getRecipientUid()`**

    The recipient source and the uid of the recipient, taken from the `X-TYPO3MID`. If they cannot be determined, the
    identifier is empty and the uid is 0. For csv and plain sources the uid is only a running number.

-   **`getOrganisation()`**

    The organisation, which is sent in the header `Organization`.

-   **`getSiteIdentifier()`**

    The identifier of the site, whose mail settings are used.

**Example: add the headers of a one-click unsubscribe (RFC 8058)**

```php
<?php

declare(strict_types=1);

namespace Vendor\ExtensionKey\EventListener;

use MEDIAESSENZ\Mail\Events\AdditionalMailHeadersEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;

#[AsEventListener(identifier: 'vendor/extensionkey/add-unsubscribe-headers')]
final class AddUnsubscribeHeaders
{
    public function __invoke(AdditionalMailHeadersEvent $event): void
    {
        if ($event->getRecipientUid() === 0) {
            return;
        }

        // This address has to be handled by your own code. It must accept a POST request.
        $url = sprintf(
            'https://example.com/unsubscribe?mail=%d&source=%s&recipient=%d',
            $event->getMail()->getUid(),
            rawurlencode($event->getRecipientSourceIdentifier()),
            $event->getRecipientUid()
        );

        $headers = $event->getHeaders();
        $headers->addTextHeader('List-Unsubscribe', '<' . $url . '>');
        $headers->addTextHeader('List-Unsubscribe-Post', 'List-Unsubscribe=One-Click');
    }
}
```

## DeactivateRecipientsEvent {#events-deactivate-recipients}

Dispatched when a user deactivates the recipients of a mailing, whose mails were returned, in the report module.

EXT:mail ships listeners for the sources tt_address, fe_users and for csv and plain lists, see
`MEDIAESSENZ\Mail\EventListener\DeactivateAddresses`, `MEDIAESSENZ\Mail\EventListener\DeactivateFeUsers`
and `MEDIAESSENZ\Mail\EventListener\InvalidateCsvRecipients`. To deactivate the recipients of any other
source, it is necessary to register a listener for it.

-   **`getData()`**

    The returned recipients, grouped by the identifier of their recipient source. For each source the array contains
    `recipients` (for table and model sources the records of the recipients, including `uid` and `email`, for csv and plain
    sources the email addresses), `returnCodes` (the values of `MEDIAESSENZ\Mail\Type\Enumeration\BounceReason`, which
    were used to select them, empty for all) and `configuration`
    (the recipient source configuration).

-   **`getRecipientSources()`**

    All recipient source configurations of the site, with their identifier as key.

-   **`getNumberOfAffectedRecipients()` and `setNumberOfAffectedRecipients()`**

    The number of recipients, which were deactivated so far. It is shown to the user. Every listener adds the number of
    the recipients it deactivated.

**Example: deactivate the recipients of an own table**

```php
<?php

declare(strict_types=1);

namespace Vendor\ExtensionKey\EventListener;

use MEDIAESSENZ\Mail\Events\DeactivateRecipientsEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;
use TYPO3\CMS\Core\Database\ConnectionPool;

#[AsEventListener(identifier: 'vendor/extensionkey/deactivate-subscribers')]
final class DeactivateSubscribers
{
    private const RECIPIENT_SOURCE = 'tx_myextension_subscriber';

    public function __construct(private readonly ConnectionPool $connectionPool)
    {
    }

    public function __invoke(DeactivateRecipientsEvent $event): void
    {
        $recipients = $event->getData()[self::RECIPIENT_SOURCE]['recipients'] ?? [];
        $connection = $this->connectionPool->getConnectionForTable(self::RECIPIENT_SOURCE);

        $affected = 0;
        foreach ($recipients as $recipient) {
            $affected += $connection->update(self::RECIPIENT_SOURCE, ['mail_active' => 0], ['uid' => (int)$recipient['uid']]);
        }

        $event->setNumberOfAffectedRecipients($event->getNumberOfAffectedRecipients() + $affected);
    }
}
```

## ManipulateCsvImportDataEvent {#events-manipulate-csv-import-data}

Dispatched during the csv import, right before the records are written to the database with the `DataHandler`. It
adds the possibility to change the data of the imported records. This needs tt_address to be installed.

-   **`getData()` and `setData()`**

    The data in the format of a `DataHandler` datamap: `[table => ['NEW1' => [field => value, ...], ...]]`. The
    table is usually `tt_address`.

-   **`getConfiguration()`**

    The configuration of the import, which consists of the page TSconfig settings of the import and the values chosen in
    the import wizard. It cannot be changed.

**Example: add a note to every imported address**

```php
<?php

declare(strict_types=1);

namespace Vendor\ExtensionKey\EventListener;

use MEDIAESSENZ\Mail\Events\ManipulateCsvImportDataEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;

#[AsEventListener(identifier: 'vendor/extensionkey/mark-imported-addresses')]
final class MarkImportedAddresses
{
    public function __invoke(ManipulateCsvImportDataEvent $event): void
    {
        $data = $event->getData();

        foreach ($data['tt_address'] ?? [] as $id => $record) {
            $data['tt_address'][$id]['description'] = 'Imported on ' . date('Y-m-d');
        }

        $event->setData($data);
    }
}
```

## ManipulateImportFolderEvent {#events-manipulate-import-folder}

Dispatched when the folder for the uploaded csv files is determined. By default this is the default upload folder of
the backend user, or the first folder of one of their file mounts. The csv files are stored in a sub folder
`importexport` of the folder, which is set here.

-   **`getImportFolder()` and `setImportFolder()`**

    The folder (`TYPO3\CMS\Core\Resource\Folder`) or `null`, if the backend user has no folder.

**Example: store the csv files in an own folder**

```php
<?php

declare(strict_types=1);

namespace Vendor\ExtensionKey\EventListener;

use MEDIAESSENZ\Mail\Events\ManipulateImportFolderEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;
use TYPO3\CMS\Core\Resource\Exception\FolderDoesNotExistException;
use TYPO3\CMS\Core\Resource\ResourceFactory;

#[AsEventListener(identifier: 'vendor/extensionkey/use-newsletter-import-folder')]
final class UseNewsletterImportFolder
{
    public function __construct(private readonly ResourceFactory $resourceFactory)
    {
    }

    public function __invoke(ManipulateImportFolderEvent $event): void
    {
        try {
            // the folder "newsletter" of the storage with the uid 1, which is usually fileadmin
            $event->setImportFolder($this->resourceFactory->getFolderObjectFromCombinedIdentifier('1:/newsletter/'));
        } catch (FolderDoesNotExistException) {
            // keep the folder of the user
        }
    }
}
```

## ManipulateImportStorageFolderEvent {#events-manipulate-import-storage-folder}

Dispatched when the folders are collected, which the user can choose in the csv import to store the imported records.
These are the folders (pages of the type folder) the backend user is allowed to edit.

-   **`getSubFolders()` and `setSubFolders()`**

    The pages of these folders as an array of records. Each record contains at least the `uid` and the `title`.

**Example: only offer folders whose title starts with "Newsletter"**

```php
<?php

declare(strict_types=1);

namespace Vendor\ExtensionKey\EventListener;

use MEDIAESSENZ\Mail\Events\ManipulateImportStorageFolderEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;

#[AsEventListener(identifier: 'vendor/extensionkey/limit-import-storage-folders')]
final class LimitImportStorageFolders
{
    public function __invoke(ManipulateImportStorageFolderEvent $event): void
    {
        $event->setSubFolders(array_values(array_filter(
            $event->getSubFolders(),
            static fn (array $folder): bool => str_starts_with((string)$folder['title'], 'Newsletter')
        )));
    }
}
```

## ManipulateMailRecipientsEvent {#events-manipulate-mail-recipients}

Dispatched when the recipients of a mailing are determined. This happens when the mailing is finished in the mail wizard
and once more when the delivery starts, because the recipients are calculated again then. Only if the extension setting
`specifyFinalRecipientsInWizard` is checked, the list from the wizard is kept. It adds the possibility to change the
recipients list.

-   **`getMail()`**

    The mailing (`MEDIAESSENZ\Mail\Domain\Model\Mail`).

-   **`getRecipients()` and `setRecipients()`**

    The recipients grouped by the identifier of their recipient source. For table and model sources the list contains
    the uids of the records, for csv and plain sources the recipients as arrays (`email`, `name` and the other fields).

**Example: never send to some addresses of tt_address**

```php
<?php

declare(strict_types=1);

namespace Vendor\ExtensionKey\EventListener;

use MEDIAESSENZ\Mail\Events\ManipulateMailRecipientsEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;

#[AsEventListener(identifier: 'vendor/extensionkey/remove-blocked-addresses')]
final class RemoveBlockedAddresses
{
    // the uids of the addresses, which must not get any newsletter
    private const BLOCKED_UIDS = [17, 42];

    public function __invoke(ManipulateMailRecipientsEvent $event): void
    {
        $recipients = $event->getRecipients();

        if (isset($recipients['tt_address'])) {
            $recipients['tt_address'] = array_values(array_diff($recipients['tt_address'], self::BLOCKED_UIDS));
        }

        $event->setRecipients($recipients);
    }
}
```

> [!TIP]
> Usage example (filtering out recipient duplicates):
>
> -   [https://gitlab.com/mediaessenz/additional-mail-recipients/-/blob/main/Classes/EventListener/FilterDuplicateRecipients.php](https://gitlab.com/mediaessenz/additional-mail-recipients/-/blob/main/Classes/EventListener/FilterDuplicateRecipients.php)
> -   [https://gitlab.com/mediaessenz/additional-mail-recipients/-/blob/main/Configuration/Services.yaml](https://gitlab.com/mediaessenz/additional-mail-recipients/-/blob/main/Configuration/Services.yaml)
>
> Attention: This example does not take into account that a duplicate recipient may have other categories.

## ManipulateMarkersEvent {#events-manipulate-markers}

Dispatched whenever the markers (placeholders) of a recipient are replaced in the content of a mail. As the HTML and the
plain text part are replaced separately, the event is dispatched twice for each recipient. It adds the possibility to add
markers, for example a salutation, or to change their values.

-   **`getMarkers()` and `setMarkers()`**

    The markers as an array. The keys are the markers including the hashes, for example `###USER_name###`, the values are
    the texts they are replaced with.

-   **`getRecipient()`**

    The data of the recipient.

-   **`getRecipientSourceConfiguration()`**

    The [recipient source configuration](https://docs.typo3.org/permalink/mediaessenz/mail:events-recipient-source-configuration@4.3) of the recipient.

EXT:mail uses this event itself to add the markers in upper case, see
`MEDIAESSENZ\Mail\EventListener\AddUpperCaseMarkers`.

**Example: add the marker ###USER_salutation### depending on the gender of tt_address**

```php
<?php

declare(strict_types=1);

namespace Vendor\ExtensionKey\EventListener;

use MEDIAESSENZ\Mail\Events\ManipulateMarkersEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;

#[AsEventListener(identifier: 'vendor/extensionkey/add-salutation-marker')]
final class AddSalutationMarker
{
    public function __invoke(ManipulateMarkersEvent $event): void
    {
        if ($event->getRecipientSourceConfiguration()->identifier !== 'tt_address') {
            return;
        }

        $markers = $event->getMarkers();
        $markers['###USER_salutation###'] = match ($event->getRecipient()['gender'] ?? '') {
            'f' => 'Dear Ms.',
            'm' => 'Dear Mr.',
            default => 'Dear',
        };

        $event->setMarkers($markers);
    }
}
```

Use the new marker in the mail content like the other ones, for example `###USER_salutation### ###USER_name###,`.

## ManipulateRecipientEvent {#events-manipulate-recipient}

Dispatched whenever the data of a recipient is loaded: for the recipient lists in the backend, right before a mail is sent
to the recipient and when a tracked link is resolved. This way all of them work with the same data. It adds the
possibility to change or complete the data of a recipient.

-   **`getRecipientData()` and `setRecipientData()`**

    The data of the recipient as an array, for example `uid`, `email` and `name`.

-   **`getRecipientSourceIdentifier()`**

    The identifier of the recipient source, for example `tt_address`.

-   **`getRecipientSourceConfiguration()`**

    The [recipient source configuration](https://docs.typo3.org/permalink/mediaessenz/mail:events-recipient-source-configuration@4.3) of the recipient.

EXT:mail uses this event itself to compose the name from the first, middle and last name
(`MEDIAESSENZ\Mail\EventListener\NormalizeRecipientData`) and to add the phone number of frontend users
(`MEDIAESSENZ\Mail\EventListener\ManipulateFrontendUserRecipient`).

**Example: use a fallback if an address has no name**

```php
<?php

declare(strict_types=1);

namespace Vendor\ExtensionKey\EventListener;

use MEDIAESSENZ\Mail\Events\ManipulateRecipientEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;

#[AsEventListener(
    identifier: 'vendor/extensionkey/name-fallback',
    after: 'mediaessenz/mail-normalize-recipient-data'
)]
final class NameFallback
{
    public function __invoke(ManipulateRecipientEvent $event): void
    {
        if ($event->getRecipientSourceIdentifier() !== 'tt_address') {
            return;
        }

        $recipientData = $event->getRecipientData();
        if (trim((string)($recipientData['name'] ?? '')) === '') {
            $recipientData['name'] = 'customer';
            $event->setRecipientData($recipientData);
        }
    }
}
```

## RecipientBouncedEvent {#events-recipient-bounced}

Is dispatched by the command [mail:analyze-bounce-mail](https://docs.typo3.org/permalink/mediaessenz/mail:analyze-bounce-mail-command-controller@4.3) after a returned
mail was analysed and stored in the log of the mailing. It is not dispatched for auto replies, which are ignored, or for
messages that cannot be assigned to a mailing.

EXT:mail does not change the recipient record (tt_address, fe_users, a csv list and so on). Use this event, if a
recipient should be deactivated right after a permanent problem, without waiting for the manual action in the report
module.

The event provides:

-   **`getType()`**

    `BounceType::HARD` for a permanent problem (status 5.x.x), `BounceType::SOFT` for a temporary one (status
    4.x.x, for example greylisting or a mailbox that is temporarily full).

-   **`getReason()`**

    A `BounceReason`: `RECIPIENT_UNKNOWN`, `MAILBOX_FULL`, `HOST_UNREACHABLE`, `POLICY_BLOCKED`
    (rejected as spam or by a policy, the recipient itself is fine), `MESSAGE_ERROR` or `UNKNOWN`.

-   **`getSmtpCode()` and `getDsnStatus()`**

    The SMTP reply code (e.g. `550`) and the enhanced status code (e.g. `5.1.1`), which the receiving server reported.
    They are `0` or empty, if the returned mail contains none.

-   **`getReasonText()`**

    The explanation of the receiving server.

-   **`getRecipientSourceIdentifier()`, `getRecipientUid()`, `getEmail()` and `getMailUid()`**

    The recipient, to whom the mail was sent, and the mailing. For sources without records, like a csv or plain list,
    the uid is only a running number of the list. A listener should ignore such sources.

**Example: deactivate a tt_address record after a permanent bounce**

```php
<?php

declare(strict_types=1);

namespace Vendor\ExtensionKey\EventListener;

use MEDIAESSENZ\Mail\Events\RecipientBouncedEvent;
use MEDIAESSENZ\Mail\Type\Enumeration\BounceReason;
use MEDIAESSENZ\Mail\Type\Enumeration\BounceType;
use TYPO3\CMS\Core\Attribute\AsEventListener;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Utility\GeneralUtility;

#[AsEventListener(identifier: 'vendor/extensionkey/deactivate-bounced-address')]
final class DeactivateBouncedAddress
{
    public function __invoke(RecipientBouncedEvent $event): void
    {
        if ($event->getRecipientSourceIdentifier() !== 'tt_address'
            || $event->getType() !== BounceType::HARD
            || $event->getReason() !== BounceReason::RECIPIENT_UNKNOWN
        ) {
            return;
        }

        $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
        $dataHandler->start(['tt_address' => [$event->getRecipientUid() => ['hidden' => 1]]], []);
        $dataHandler->process_datamap();
    }
}
```

> [!NOTE]
> Only deactivate recipients after a hard bounce with the reason `RECIPIENT_UNKNOWN`. A full mailbox, a rejection
> by a spam filter or a temporary problem says nothing about the address itself.

## RecipientEmailCorrectedEvent {#events-recipient-email-corrected}

Is dispatched after the email address of a failed or waiting delivery was corrected in the
[queue module](https://docs.typo3.org/permalink/mediaessenz/mail:send-queue-backend@4.3). The correction has to be enabled with the extension setting
[enableEmailCorrection](https://docs.typo3.org/permalink/mediaessenz/mail:extension-configuration-enable-email-correction@4.3), which is off by default. Without it, no
address can be changed and the event is never dispatched.

The correction only applies to this delivery. EXT:mail does not change the recipient record (tt_address, fe_users, a
csv list and so on). Use this event, if the corrected address should be applied to the recipient record as well.

The event is only dispatched, if the address was really changed. It is dispatched after the queue entry was saved, so
an exception in a listener cannot undo the correction.

The event provides:

-   **`getRecipientSourceIdentifier()`**

    The identifier of the recipient source, e.g. `tt_address` or `fe_users`, like in the recipient sources configuration
    of your site.

-   **`getRecipientUid()`**

    The uid of the record in this source. For sources without records, like a csv or plain list, this is only a running
    number of the list. A listener should ignore such sources.

-   **`getOldEmail()` and `getNewEmail()`**

    The address before and after the correction.

-   **`getMailUid()`, `getQueueItemUid()` and `getRecipientData()`**

    The mail, the queue entry and the recipient data of the queue entry, which already contains the new address.

**Example: apply the corrected address to a tt_address record**

```php
<?php

declare(strict_types=1);

namespace Vendor\ExtensionKey\EventListener;

use MEDIAESSENZ\Mail\Events\RecipientEmailCorrectedEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Utility\GeneralUtility;

#[AsEventListener(identifier: 'vendor/extensionkey/apply-corrected-email-to-address')]
final class ApplyCorrectedEmailToAddress
{
    public function __invoke(RecipientEmailCorrectedEvent $event): void
    {
        if ($event->getRecipientSourceIdentifier() !== 'tt_address') {
            return;
        }

        $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
        $dataHandler->start(
            ['tt_address' => [$event->getRecipientUid() => ['email' => $event->getNewEmail()]]],
            []
        );
        $dataHandler->process_datamap();
    }
}
```

> [!NOTE]
> The example uses the `DataHandler`. This way the change is done with the permissions of the backend user who
> corrected the address, it is logged in the record history, and caches are cleared. How a listener is registered is
> described [above](https://docs.typo3.org/permalink/mediaessenz/mail:events-register-listener@4.3).

## RecipientsRestrictionEvent {#events-recipients-restriction}

Dispatched when the query for the recipients of an Extbase model source is built. It adds the possibility to restrict
the recipients with further constraints. EXT:mail has already added the constraints for active recipients, a not empty
email address and the selected categories.

-   **`getRecipientSourceConfiguration()`**

    The [recipient source configuration](https://docs.typo3.org/permalink/mediaessenz/mail:events-recipient-source-configuration@4.3) of the source.

-   **`getQuery()`**

    The `TYPO3\CMS\Extbase\Persistence\QueryInterface`, which is used to create new constraints.

-   **`getConstraints()` and `setConstraints()`**

    The constraints, which are combined with a logical and. Add your constraints to this list.

**Example: exclude all addresses of a domain**

```php
<?php

declare(strict_types=1);

namespace Vendor\ExtensionKey\EventListener;

use MEDIAESSENZ\Mail\Events\RecipientsRestrictionEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;

#[AsEventListener(identifier: 'vendor/extensionkey/exclude-domain')]
final class ExcludeDomain
{
    public function __invoke(RecipientsRestrictionEvent $event): void
    {
        if ($event->getRecipientSourceConfiguration()->identifier !== 'tt_address') {
            return;
        }

        $query = $event->getQuery();
        $constraints = $event->getConstraints();
        $constraints[] = $query->logicalNot($query->like('email', '%@example.invalid'));

        $event->setConstraints($constraints);
    }
}
```

## ScheduledSendBegunEvent {#events-scheduled-send-begun}

Dispatched once for each mailing, when its delivery begins. This is before the start is stored in the mailing and
before the recipients are calculated, so the number of recipients is not final yet.

-   **`getMail()` and `setMail()`**

    The mailing (`MEDIAESSENZ\Mail\Domain\Model\Mail`).

**Example: write a log entry when a mailing begins**

```php
<?php

declare(strict_types=1);

namespace Vendor\ExtensionKey\EventListener;

use MEDIAESSENZ\Mail\Events\ScheduledSendBegunEvent;
use Psr\Log\LoggerInterface;
use TYPO3\CMS\Core\Attribute\AsEventListener;

#[AsEventListener(identifier: 'vendor/extensionkey/log-send-begun')]
final class LogSendBegun
{
    public function __construct(private readonly LoggerInterface $logger)
    {
    }

    public function __invoke(ScheduledSendBegunEvent $event): void
    {
        $mail = $event->getMail();
        $this->logger->info('Mailing {uid} "{subject}" begins to send', [
            'uid' => $mail->getUid(),
            'subject' => $mail->getSubject(),
        ]);
    }
}
```

## ScheduledSendFinishedEvent {#events-scheduled-send-finished}

Dispatched once for each mailing, when its delivery is finished, either because all recipients were handled or because
the mailing has no recipients. This is before the end is stored in the mailing.

-   **`getMail()` and `setMail()`**

    The mailing (`MEDIAESSENZ\Mail\Domain\Model\Mail`).

**Example: write a log entry when a mailing is finished**

```php
<?php

declare(strict_types=1);

namespace Vendor\ExtensionKey\EventListener;

use MEDIAESSENZ\Mail\Events\ScheduledSendFinishedEvent;
use Psr\Log\LoggerInterface;
use TYPO3\CMS\Core\Attribute\AsEventListener;

#[AsEventListener(identifier: 'vendor/extensionkey/log-send-finished')]
final class LogSendFinished
{
    public function __construct(private readonly LoggerInterface $logger)
    {
    }

    public function __invoke(ScheduledSendFinishedEvent $event): void
    {
        $mail = $event->getMail();
        $this->logger->info('Mailing {uid} "{subject}" finished: {handled} of {total} recipients handled', [
            'uid' => $mail->getUid(),
            'subject' => $mail->getSubject(),
            'handled' => $mail->getNumberOfRecipientsHandled(),
            'total' => $mail->getNumberOfRecipients(),
        ]);
    }
}
```

### Need more? {#need-more}

Please contact me
