async send(data, options) {
        const {
            subject,
            html,
            plaintext,
            from,
            replyTo,
            emailId,
            recipients,
            replacementDefinitions
        } = data;

        logging.info(`Sending email to ${recipients.length} recipients`);
        const startTime = Date.now();
        debug(`sending message to ${recipients.length} recipients`);

        try {
            const messageData = {
                subject,
                html,
                plaintext,
                from,
                replyTo,
                id: emailId,
                track_opens: !!options.openTrackingEnabled,
                track_clicks: !!options.clickTrackingEnabled
            };

            if (options.deliveryTime && options.deliveryTime instanceof Date) {
                messageData.deliveryTime = options.deliveryTime;
            }

            // create recipient data for Mailgun using replacement definitions
            const recipientData = recipients.reduce((acc, recipient) => {
                acc[recipient.email] = this.#createRecipientData(recipient.replacements);
                return acc;
            }, {});

            // update content to use Mailgun variable syntax for all replacements
            ['html', 'plaintext'].forEach((key) => {
                if (messageData[key]) {
                    messageData[key] = this.#updateRecipientVariables(messageData[key], replacementDefinitions);
                }
            });

            // send the email using Mailgun
            // uses empty replacements array as we've already replaced all tokens with Mailgun variables
            const response = await this.#mailgunClient.send(
                messageData,
                recipientData,
                []
            );

            debug(`sent message (${Date.now() - startTime}ms)`);
            logging.info(`Sent message (${Date.now() - startTime}ms)`);

            // Return mailgun provider id, trim <> from response
            return {
                id: response.id.trim().replace(/^<|>$/g, '')
            };
        } catch (e) {
            let ghostError;
            if (e.error && e.messageData) {
                const {error, messageData} = e;

                // REF: possible mailgun errors https://documentation.mailgun.com/en/latest/api-intro.html#status-codes
                ghostError = new errors.EmailError({
                    statusCode: error.status,
                    message: this.#createMailgunErrorMessage(error),
                    errorDetails: JSON.stringify({error, messageData}),
                    context: `Mailgun Error ${error.status}: ${error.details}`,
                    help: `https://ghost.org/docs/newsletters/#bulk-email-configuration`,
                    code: 'BULK_EMAIL_SEND_FAILED'
                });
            } else {
                ghostError = new errors.EmailError({
                    statusCode: undefined,
                    message: this.#createMailgunErrorMessage(e),
                    errorDetails: undefined,
                    context: e.context || 'Mailgun Error',
                    code: 'BULK_EMAIL_SEND_FAILED'
                });
            }

            debug(`failed to send message (${Date.now() - startTime}ms)`);

            throw ghostError;
        }
    }
Send an email using the Mailgun API
@param {import('./SendingService').EmailData} data
@param {EmailSendingOptions} options
@returns {Promise<EmailProviderSuccessResponse>}