Send email with PHPMailer SMTP when your PHP application needs a portable, standards-based way to deliver transactional messages through Volanea. This guide installs PHPMailer with Composer, loads SMTP credentials from environment variables, and sends one HTML-and-text email.

What this integration uses

PHPMailer is a PHP library for creating MIME email messages and sending them through SMTP. In this integration, PHPMailer creates the message in your application, opens a secure SMTP connection to the Volanea relay, authenticates with the SMTP credentials assigned to your account, submits the message, and reports whether the relay accepted it.

This is an SMTP integration, not an HTTP API client. That distinction matters when you configure credentials:

  • PHPMailer uses an SMTP hostname, port, encryption mode, username, and password.
  • Keep every credential in environment variables rather than committing it to source control.
  • The variable named VOLANEA_API_KEY in the sample below is used as the SMTP secret only if you intentionally store your Volanea-provided SMTP password in that variable. It is an environment-variable name, not a claim that every Volanea REST API secret is automatically an SMTP password.
  • Use the SMTP host, port, TLS mode, username, and password currently assigned to your Volanea account. Do not guess a hostname, port, or authentication format from another email provider.

SMTP is useful when your PHP application already uses PHPMailer, when you want to keep your mail-sending code provider-neutral, or when a framework/plugin expects an SMTP server. The application-level work is familiar: set a verified sender, choose recipients, provide a subject, include a plain-text alternative, and call send().

The relay acceptance result is important, but it is not the same as inbox placement. A successful send() call means the SMTP relay accepted the message for processing. Recipient-side delivery can still be affected by recipient address validity, authentication alignment, mailbox policy, and suppressions.

Prerequisites

Before running the example, have the following ready:

  1. PHP and Composer. The sample uses Composer’s autoloader and the phpmailer/phpmailer package.
  2. A Volanea account with SMTP sending configured. Retrieve the SMTP connection details supplied for your account rather than substituting values from a different provider.
  3. A sender address on a verified sending domain. The address used in setFrom() should belong to a domain authorized for your Volanea configuration.
  4. An SMTP username and SMTP password. These are separate from the email address that appears in the From header unless your account configuration explicitly makes them the same.
  5. A recipient address you control for the first test. This lets you inspect the received message and prevents accidental customer-facing test sends.
  6. Outbound network access to the configured SMTP port. Some hosting providers, containers, serverless environments, and corporate networks restrict outbound SMTP connections.

For the first send, use a simple subject and a low-risk recipient. Do not use a production mailing list to validate a new SMTP configuration. A failed first test is easier to investigate when there is one message, one recipient, and a known sender.

Keep secrets outside your repository

Your SMTP password is a secret. Treat it like any other production credential:

  • Do not hard-code it in PHP files.
  • Do not add it to a committed .env file.
  • Do not paste it into application logs, exception pages, tickets, or screenshots.
  • Rotate it if you believe it has been exposed.
  • Give local development, staging, and production separate credentials where your account setup supports that operational model.

The code below reads configuration with getenv(). This works with shell-exported variables, container environment variables, many PaaS secret managers, and process managers that inject environment variables into PHP.

Install PHPMailer

From the root directory of your PHP project, install PHPMailer with Composer:

composer require phpmailer/phpmailer

Composer downloads the package into vendor/ and creates vendor/autoload.php. The code sample loads that generated autoloader; do not manually require PHPMailer class files when you are using Composer.

Confirm that Composer sees the dependency:

composer show phpmailer/phpmailer

If the command cannot find Composer, install Composer for the PHP runtime used by your project, then rerun the install command. Be careful when a web server runs a different PHP binary from the one invoked in your terminal: an extension, certificate bundle, or environment variable visible to CLI PHP may not be available to PHP-FPM or Apache.

Configure Volanea SMTP environment variables

Set the following variables in the environment where the PHP process runs. Replace the placeholder values with the exact SMTP settings provided for your Volanea account.

export VOLANEA_SMTP_HOST='your-volanea-smtp-host'
export VOLANEA_SMTP_PORT='587'
export VOLANEA_SMTP_USERNAME='your-volanea-smtp-username'
export VOLANEA_API_KEY='your-volanea-smtp-password'
export VOLANEA_SMTP_ENCRYPTION='tls'
export VOLANEA_FROM_EMAIL='receipts@your-verified-domain.com'
export VOLANEA_FROM_NAME='Example Store'
export TEST_RECIPIENT_EMAIL='you@example.com'

The sample uses VOLANEA_API_KEY because many deployment systems already standardize on that secret name. For SMTP, the value must be the credential that Volanea designates as the SMTP password for this connection. If your team distinguishes REST API keys from SMTP passwords, rename the variable to VOLANEA_SMTP_PASSWORD in both your secret manager and the PHP sample. The important behavior is that a secret is loaded from the environment, never embedded in the source file.

Choose the encryption setting that matches your connection details

The sample supports two common SMTP transport modes:

  • Set VOLANEA_SMTP_ENCRYPTION='tls' for explicit TLS via STARTTLS. This is commonly paired with port 587, but use the port and mode assigned to your account.
  • Set VOLANEA_SMTP_ENCRYPTION='smtps' for implicit TLS. This is commonly paired with port 465, but again, follow the connection settings supplied to you.

Do not set encryption to none for production email. A mismatch between TLS mode and port is one of the most common reasons an SMTP connection fails before authentication.

For local testing only, you can export variables for the current terminal session and run the script from that same shell. In production, set them in the platform’s secret manager, container specification, PHP-FPM pool configuration, or deployment environment. Avoid relying on a development-only dotenv loader unless your application has explicitly installed and configured one.

Complete PHPMailer SMTP example

Create a file named send-email.php in your project root. This is a complete script: it validates required configuration, initializes PHPMailer for SMTP, uses credentials from environment variables, sends one transactional email, prints the provider-assigned message identifier when available, and returns a non-zero exit code on failure.

<?php

declare(strict_types=1);

use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\PHPMailer;

require __DIR__ . '/vendor/autoload.php';

/**
 * Read a required environment variable and fail before attempting SMTP.
 */
function requiredEnv(string $name): string
{
    $value = getenv($name);

    if ($value === false || trim($value) === '') {
        throw new RuntimeException("Missing required environment variable: {$name}");
    }

    return trim($value);
}

try {
    $host = requiredEnv('VOLANEA_SMTP_HOST');
    $port = (int) requiredEnv('VOLANEA_SMTP_PORT');
    $username = requiredEnv('VOLANEA_SMTP_USERNAME');

    // Store the Volanea SMTP password in this environment variable.
    // If your deployment uses VOLANEA_SMTP_PASSWORD instead, change this line.
    $smtpPassword = requiredEnv('VOLANEA_API_KEY');

    $encryption = strtolower(requiredEnv('VOLANEA_SMTP_ENCRYPTION'));
    $fromEmail = requiredEnv('VOLANEA_FROM_EMAIL');
    $fromName = requiredEnv('VOLANEA_FROM_NAME');
    $recipientEmail = requiredEnv('TEST_RECIPIENT_EMAIL');

    if ($port < 1 || $port > 65535) {
        throw new RuntimeException('VOLANEA_SMTP_PORT must be a valid TCP port number.');
    }

    if (!in_array($encryption, ['tls', 'smtps'], true)) {
        throw new RuntimeException(
            "VOLANEA_SMTP_ENCRYPTION must be either 'tls' or 'smtps'."
        );
    }

    $mail = new PHPMailer(true);

    // Tell PHPMailer to use SMTP rather than PHP's local mail() transport.
    $mail->isSMTP();
    $mail->Host = $host;
    $mail->Port = $port;
    $mail->SMTPAuth = true;
    $mail->Username = $username;
    $mail->Password = $smtpPassword;
    $mail->CharSet = PHPMailer::CHARSET_UTF8;

    // Use the TLS mode assigned to your Volanea SMTP connection.
    $mail->SMTPSecure = $encryption === 'smtps'
        ? PHPMailer::ENCRYPTION_SMTPS
        : PHPMailer::ENCRYPTION_STARTTLS;

    // Keep SMTP protocol output out of normal application logs.
    $mail->SMTPDebug = 0;

    // The From address must be permitted by your configured sending domain.
    $mail->setFrom($fromEmail, $fromName);
    $mail->addAddress($recipientEmail, 'Test Recipient');
    $mail->addReplyTo('support@your-verified-domain.com', 'Example Store Support');

    $mail->isHTML(true);
    $mail->Subject = 'Your Example Store receipt';
    $mail->Body = <<<'HTML'
<!doctype html>
<html lang="en">
  <body>
    <h1>Thanks for your order</h1>
    <p>Your receipt is ready. This is a transactional SMTP test sent with PHPMailer.</p>
  </body>
</html>
HTML;
    $mail->AltBody = 'Thanks for your order. Your receipt is ready. This is a transactional SMTP test sent with PHPMailer.';

    $mail->send();

    echo "Email accepted by SMTP relay. Message ID: {$mail->getLastMessageID()}" . PHP_EOL;
} catch (Exception $exception) {
    fwrite(STDERR, 'PHPMailer error: ' . $exception->getMessage() . PHP_EOL);
    exit(1);
} catch (RuntimeException $exception) {
    fwrite(STDERR, 'Configuration error: ' . $exception->getMessage() . PHP_EOL);
    exit(1);
}

Replace support@your-verified-domain.com before running the script. If you do not need replies for this message, remove the addReplyTo() line instead. Leaving a placeholder Reply-To address in production is a common support and deliverability mistake.

Run the script in the same environment where the variables are available:

php send-email.php

On success, the script prints the message ID generated by PHPMailer. Record that value with your own application request ID or order ID when practical. It is useful for support investigations, but it is not a substitute for durable application-level event records.

How the example works

SMTP transport and authentication

$mail->isSMTP() selects PHPMailer’s SMTP transport. Without it, PHPMailer can use a local transport such as PHP’s mail() function, which bypasses the Volanea SMTP settings and produces confusing results in environments without a configured local mail transfer agent.

Host, Port, SMTPAuth, Username, and Password describe the outbound SMTP connection. All five values must match the connection settings assigned to your Volanea account. If any one is wrong, PHPMailer may fail while connecting, during TLS negotiation, or while authenticating.

The sample does not attempt to infer credentials from the From address. SMTP authentication establishes whether the application can use the relay; the From address establishes the identity presented to recipients. Both must be configured correctly.

TLS selection

PHPMailer exposes two relevant secure SMTP constants:

  • PHPMailer::ENCRYPTION_STARTTLS asks the server to upgrade the connection with STARTTLS after connecting.
  • PHPMailer::ENCRYPTION_SMTPS begins with TLS immediately.

These are not interchangeable. Use the encryption mode given in the Volanea connection details. For example, forcing implicit TLS against a server expecting STARTTLS can result in a handshake or connection error before your username and password are evaluated.

The example intentionally does not disable certificate verification. Avoid snippets that set permissive SSL options merely to make a certificate error disappear. Certificate errors usually indicate a bad hostname, a proxy intercepting TLS, an outdated CA bundle, or a server clock issue that should be fixed at the environment level.

HTML and plain-text bodies

$mail->isHTML(true) tells PHPMailer to build an HTML message body. Body contains the HTML version, while AltBody contains a readable plain-text alternative. Include both for transactional mail whenever possible.

A plain-text alternative is valuable for recipients using text-oriented clients, for security scanners, and when HTML rendering is unavailable. It also forces you to make sure that the essential transaction information is not hidden inside an image, a button, or a complex layout.

The isHTML(true) call is the SMTP equivalent of choosing an HTML message body. You do not add an HTTP Content-Type: application/json header to this flow. SMTP is not a REST request, and manually adding HTTP headers is wrong for an SMTP message.

Sender and recipient addresses

setFrom() controls the visible From header. Use an address on a sending domain you have configured and verified. Do not accept a From address directly from an untrusted HTTP request, form field, or API payload; doing so can produce spoofed or unauthorized identities.

addAddress() adds a recipient. PHPMailer can also add CC and BCC recipients, but a single-recipient test is the best starting point. Validate and authorize recipient selection in your application before constructing the message, especially for password resets, account alerts, invoices, and administrative notifications.

addReplyTo() is optional but often useful. It should route to an inbox or support workflow that is actually monitored. A Reply-To address is not an authentication workaround and should not be used to disguise a sender domain that has not been configured.

Use the sample in an application, not only from the command line

The standalone file proves that credentials and network access work. In an application, put the message-construction and sending logic behind a small service rather than scattering SMTP configuration throughout controllers, route handlers, cron jobs, and queue workers.

A useful boundary looks like this:

  1. Application code creates a domain event, such as PasswordResetRequested or OrderReceiptRequested.
  2. A mail service receives a prevalidated recipient, sender, subject, HTML body, and plain-text body.
  3. The mail service reads its SMTP configuration once from the runtime environment.
  4. The service sends through PHPMailer and records either the acceptance result or the exception.
  5. A retry mechanism handles temporary failures without repeatedly sending the same business event.

This structure helps prevent email delivery details from leaking into business logic. It also makes it easier to test: unit tests can verify that a receipt request produces the correct subject and content, while an integration environment can exercise the actual SMTP connection.

Avoid duplicate transactional messages

SMTP does not provide a universal, end-to-end exactly-once guarantee for your business action. A process can time out after the relay has accepted a message but before your application records success. Blindly retrying then risks a duplicate receipt, alert, or password-reset email.

For important sends, store an application-level idempotency record before or alongside the dispatch attempt. For example, save a unique key such as receipt:order_12345:v1, record its status, and only send if that key has not already reached a terminal accepted state. Your policy should distinguish between a definite connection failure, a definite authentication failure, and an ambiguous timeout.

Do not solve duplicate sends by removing retries entirely. Instead, make retries deliberate and based on the type of failure. Authentication errors and malformed addresses generally require a configuration or data fix; transient network failures may warrant a bounded retry with backoff.

Keep email sending off latency-sensitive requests when necessary

For a small application, sending inside a web request may be acceptable. As traffic grows, SMTP network calls can make page loads and API responses slower, especially during temporary network trouble. A background job or queue worker lets the request complete after reliably recording the intent to send.

If you queue email work, include the data needed to render the message safely, but avoid persisting unnecessary sensitive content. Password-reset links, invoice data, and account details need appropriate retention controls. Also ensure workers load the same environment variables and CA certificates as your web processes; a working local CLI test does not prove the worker runtime is configured.

Common errors

Authentication fails with an SMTP username or password error

Symptoms often include an SMTP authentication failure, a 535-style response, or a PHPMailer exception that mentions authentication.

Check these items in order:

  • Confirm that VOLANEA_SMTP_USERNAME is the exact SMTP username supplied for the account.
  • Confirm that the secret loaded into VOLANEA_API_KEY is the SMTP password assigned for that connection, not automatically a REST API key or a copied value with whitespace.
  • Check whether your deployment system has updated the secret but the PHP-FPM process, container, or worker has not been restarted.
  • Ensure SMTPAuth remains set to true.
  • Do not wrap environment values in extra quotation marks when your secret manager already treats quotes as literal characters.

To see the SMTP conversation temporarily, set $mail->SMTPDebug = 2 only in a controlled non-production environment. SMTP debug output can contain recipient addresses and protocol details. Never log the password, and turn debugging back off once you identify the issue.

Connection times out or cannot reach the SMTP host

A timeout usually occurs before authentication. It may mean the hostname is wrong, DNS cannot resolve it, the configured port is blocked, or the runtime has no outbound network route.

Verify the SMTP host and port against the Volanea account connection details. Then test from the same server, container, or worker that runs PHP—not merely from a developer laptop. Hosting providers sometimes block outbound SMTP ports by default, and container network policies can allow HTTPS while denying SMTP.

Do not change ports at random. A connection can fail differently depending on whether the port is blocked, a hostname is invalid, or encryption mode does not match the server. Match the exact host, port, and TLS mode provided for your connection.

TLS handshake or certificate verification fails

A TLS failure often points to a mismatch between VOLANEA_SMTP_ENCRYPTION and VOLANEA_SMTP_PORT, an incorrect SMTP hostname, an outdated CA certificate store, or a proxy inspecting encrypted traffic.

Set VOLANEA_SMTP_ENCRYPTION='tls' only when your account settings call for STARTTLS; set it to smtps only when they call for immediate TLS. Do not disable peer verification as a permanent workaround. Update the operating system CA bundle, correct the hostname, or resolve the network proxy configuration instead.

Check system time too. A substantially incorrect server clock can make valid certificates appear expired or not yet valid.

The From address is rejected

An SMTP relay can reject a message whose From address is not permitted for the authenticated account or whose domain has not been set up for sending. Confirm that VOLANEA_FROM_EMAIL belongs to your configured sending domain and that the address is spelled correctly.

Do not try to fix this by setting an unrelated Reply-To address or by changing only the display name. The domain portion of the From address is the key configuration detail. Use a sender identity that your application owns and is authorized to use.

HTML arrives as literal markup or renders poorly

If recipients see <h1> tags instead of formatted content, confirm that $mail->isHTML(true) runs before send(). Keep AltBody as plain text; it is intentionally not HTML.

This is sometimes described as a “wrong content-type” problem, but SMTP email is not configured with an HTTP request Content-Type header. PHPMailer creates the email MIME structure from methods such as isHTML(true), Body, and AltBody. Do not add Content-Type: application/json or other HTTP headers to the message.

For rendering problems, use complete but simple HTML, inline styles when needed for broad email-client compatibility, and a tested plain-text alternative. Avoid relying on JavaScript, external stylesheets, or complex browser-only layout behavior.

The script says an environment variable is missing

The script intentionally fails early if any required variable is absent. This is safer than trying to connect with blank credentials.

First, run printenv | grep VOLANEA from the same shell as php send-email.php. In a web application, remember that Apache, PHP-FPM, queue workers, and cron often have different environments from an interactive shell. Configure variables at the process-manager or deployment level and restart the affected service.

On shared hosting, getenv() availability can depend on the host configuration. If your platform provides a documented secrets mechanism, use that mechanism and make sure its values are exposed to the PHP runtime.

You expected async/await behavior

PHP and PHPMailer do not use JavaScript’s async/await syntax. $mail->send() is synchronous: the current PHP process waits while it connects, authenticates, submits the message, and receives a relay response.

If you need asynchronous user-facing behavior, move the PHP send operation to a queue worker, scheduled task, or background-job system. Do not paste await, Promise chains, or Node.js SMTP examples into PHP. The correct design is asynchronous at the job architecture level, while the PHPMailer call within an individual worker remains synchronous.

A message is accepted but not visible in the inbox

First check the recipient’s spam or junk folder, then validate the recipient address and sender configuration. SMTP acceptance only proves that the relay accepted the submission; it does not guarantee a particular mailbox provider’s placement decision.

For a production investigation, correlate your application’s send attempt, recipient address, timestamp, message ID, and any delivery events available in your email platform. Do not repeatedly resend the same transactional email during an investigation without an idempotency strategy; that can turn an observability problem into a customer-experience problem.

Production checklist

Before relying on this integration for customer communications, review this checklist:

  • PHPMailer is installed through Composer and vendor/autoload.php is deployed.
  • SMTP hostname, port, encryption mode, username, and password match the settings assigned to the account.
  • Credentials are stored in runtime secrets, not in Git or a browser-accessible configuration file.
  • The From address belongs to a configured, verified sending domain.
  • Each HTML message includes an accurate plain-text AltBody.
  • Reply-To routes to a monitored mailbox, or it is omitted intentionally.
  • PHP has access to a current CA certificate bundle and correct system time.
  • Outbound traffic from the production runtime can reach the assigned SMTP endpoint and port.
  • Application logs record a safe correlation ID and outcome without recording credentials or full sensitive message bodies.
  • Important messages use idempotency controls and a defined retry policy.
  • Queue workers, cron processes, and web processes receive the necessary environment variables.
  • You have tested a real end-to-end send to a mailbox you control.

The checklist is deliberately operational as well as code-focused. Most production mail failures are not caused by a missing send() call; they come from credentials that differ across environments, unverified sender domains, network egress restrictions, or an application retry policy that is not designed for message delivery.

Next steps

Once the first message is working, move from a single test email to an observable sending workflow.

Webhooks: Configure delivery-event handling so your application can record message outcomes such as delivery failures, bounces, complaints, or other provider events available to your account. Treat webhook endpoints as public, security-sensitive HTTP endpoints: verify authenticity according to the documented mechanism, respond quickly, tolerate duplicate deliveries, and enqueue heavier processing rather than doing it in the request handler.

Templates: Move repeatable transactional content—receipts, invitations, verification notices, and password resets—into a controlled template workflow. Templates reduce copy-and-paste markup, make brand changes easier to review, and help ensure that the HTML and plain-text versions stay consistent. Keep the business data passed into a template minimal and validate it before rendering.

For the available API reference and setup material, see the developer documentation. If your application later benefits from an HTTP-based sending path instead of SMTP, use the documented API contract rather than attempting to adapt this PHPMailer configuration into an HTTP request.

FAQ

Can I use a Volanea REST API key as the PHPMailer SMTP password?

Only if the credentials supplied for your SMTP connection explicitly designate that secret for SMTP authentication. PHPMailer needs an SMTP username and password. Do not assume that a REST API key works as an SMTP password merely because both are secrets issued by the same platform.

Which SMTP port should I use with PHPMailer?

Use the exact port assigned in your Volanea SMTP connection settings. Port numbers are tied to the TLS mode and network policy, so do not select a port based only on another provider’s example.

Why should I set both Body and AltBody?

Body provides the HTML version of your message, while AltBody provides a plain-text alternative. Including both makes transactional messages more accessible and more resilient across email clients.

Does a successful $mail->send() call mean the email reached the inbox?

No. It means the SMTP relay accepted the message submission. Final delivery and inbox placement depend on subsequent processing, recipient address validity, authentication, recipient-server policy, and other delivery factors.

Can I call PHPMailer from a Laravel, Symfony, WordPress, or custom PHP app?

Yes, provided the application can load Composer dependencies and make an outbound SMTP connection. Many frameworks also have their own mail abstractions; use their documented SMTP transport configuration when it is a better fit, while applying the same sender, credential, TLS, and operational principles described here.