Send email with PHP through Volanea by using the standard SMTP interface and PHPMailer. This approach is portable across plain PHP applications and frameworks, keeps credentials in environment variables, and avoids depending on an unverified provider-specific PHP SDK.
What this guide sends
This guide sends one transactional email: a simple account-welcome message with both HTML and plain-text content. The example uses PHPMailer as the SMTP client, Composer for dependency installation, and a local .env file for development.
SMTP is a good fit when your PHP application already has a mail abstraction, runs on traditional hosting, or needs a dependable protocol that is not tied to one application framework. Volanea supports transactional sending over SMTP, so the application connects to the SMTP relay using the host, port, encryption mode, username, and password assigned to your account.
This is intentionally an SMTP integration rather than a fictional PHP SDK integration. A REST API key and SMTP credentials are not automatically interchangeable: use the credential type and connection details shown in your Volanea account’s current SMTP setup instructions. Do not guess a hostname, port, encryption mode, username, or authentication format.
Prerequisites
Before running the example, make sure you have the following:
- PHP 8.1 or later. The example also works with many older PHP versions supported by PHPMailer, but PHP 8.1+ is a practical baseline for current applications.
- Composer installed and available on your command line.
- The PHP OpenSSL extension enabled. SMTP encryption requires it.
- A Volanea sending domain that is ready to send from the address you choose.
- The SMTP host, port, encryption mode, username, and password for your Volanea account.
- A recipient address you can access for testing.
A successful SMTP acceptance means Volanea accepted the message for processing. It does not by itself prove that the recipient inbox received the message. Inbox placement can still be affected by authentication, recipient-server policy, reputation, suppression state, and content.
Confirm your sender before testing
Use a sender address on a domain you control, such as notifications@example.com. The domain should be configured as a sending domain before you test production traffic. Sending from a consumer mailbox address or an unconfigured domain often causes authentication failures, policy rejections, or poor inbox placement.
For a first test, send to an address you control. That lets you inspect the received message, check the visible sender, verify the plain-text fallback, and compare headers if troubleshooting is needed.
Install the PHP dependencies
Create a new directory for the example, or open your existing PHP project directory. Then install PHPMailer and phpdotenv with Composer:
composer require phpmailer/phpmailer vlucas/phpdotenv
PHPMailer handles SMTP negotiation, authentication, MIME formatting, recipient headers, and HTML/plain-text multipart messages. phpdotenv loads local development variables from a .env file so you do not hard-code secrets in PHP files.
In a framework such as Laravel or Symfony, you may already have environment-variable loading and a mailer component. In that case, keep the same SMTP concepts but use the framework’s approved configuration mechanism. Do not install duplicate mail systems merely to follow this sample verbatim.
Check required PHP extensions
Run the following command to confirm OpenSSL is available:
php -m | grep openssl
On Windows PowerShell, use:
php -m | Select-String openssl
If no result appears, enable the OpenSSL extension in the PHP installation used by your web server or CLI. It is common for the PHP binary in a terminal to use a different configuration file than PHP-FPM, Apache, or a hosting control panel.
Create your environment file
Create a file named .env in the project root. Keep it out of version control. The values below are placeholders: replace them with the SMTP values supplied for your Volanea account.
VOLANEA_SMTP_HOST=smtp.example-provider-host.invalid
VOLANEA_SMTP_PORT=587
VOLANEA_SMTP_ENCRYPTION=tls
VOLANEA_SMTP_USERNAME=replace-with-your-smtp-username
VOLANEA_SMTP_PASSWORD=replace-with-your-smtp-password
MAIL_FROM_ADDRESS=notifications@example.com
MAIL_FROM_NAME="Example App"
MAIL_TO_ADDRESS=you@example.net
MAIL_TO_NAME="Test Recipient"
Do not use smtp.example-provider-host.invalid as a real host. It is deliberately non-routable. Copy the SMTP host exactly as provided in your account configuration or the REST and SMTP setup reference.
The VOLANEA_SMTP_ENCRYPTION variable in this guide accepts either tls or smtps:
tlsmeans the client connects normally and upgrades the connection with STARTTLS. Port 587 is a common convention, but always use the port assigned in your account.smtpsmeans SMTP over an encrypted connection from the beginning. Port 465 is a common convention, but again, use the value provided for your account.
The names of these environment variables are application-level names used by this example. They are not dashboard field names. Use them to store the exact credentials and connection values assigned to you.
Keep secrets out of Git
Add .env to .gitignore before adding any credentials:
.env
/vendor/
A committed SMTP password can be used to send mail as your application. If a credential is accidentally exposed in a repository, chat transcript, ticket, browser log, deployment log, or CI output, revoke or rotate it immediately and update the deployment secret.
For production, define the same values through your hosting platform, container runtime, secrets manager, or CI/CD environment configuration. A .env file is useful for local development, but it should not be the primary secret store for a production system.
Complete PHP example
Create a file named send-email.php in the same directory as composer.json and .env. This is a complete executable script: it loads environment variables, validates required settings, opens an encrypted SMTP connection, sends one message, and exits with a useful status code.
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use Dotenv\Dotenv;
use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\PHPMailer;
// Load .env for local development. In production, configure real environment
// variables through your hosting platform or secret manager instead.
Dotenv::createImmutable(__DIR__)->safeLoad();
/**
* Return a required environment variable or stop with a clear error.
*/
function requiredEnv(string $name): string
{
$value = $_ENV[$name] ?? getenv($name);
if ($value === false || $value === null || trim((string) $value) === '') {
fwrite(STDERR, "Missing required environment variable: {$name}" . PHP_EOL);
exit(1);
}
return trim((string) $value);
}
$host = requiredEnv('VOLANEA_SMTP_HOST');
$port = (int) requiredEnv('VOLANEA_SMTP_PORT');
$encryption = strtolower(requiredEnv('VOLANEA_SMTP_ENCRYPTION'));
$username = requiredEnv('VOLANEA_SMTP_USERNAME');
$password = requiredEnv('VOLANEA_SMTP_PASSWORD');
$fromAddress = requiredEnv('MAIL_FROM_ADDRESS');
$fromName = requiredEnv('MAIL_FROM_NAME');
$toAddress = requiredEnv('MAIL_TO_ADDRESS');
$toName = requiredEnv('MAIL_TO_NAME');
if ($port < 1 || $port > 65535) {
fwrite(STDERR, "VOLANEA_SMTP_PORT must be a valid TCP port." . PHP_EOL);
exit(1);
}
if (!in_array($encryption, ['tls', 'smtps'], true)) {
fwrite(
STDERR,
"VOLANEA_SMTP_ENCRYPTION must be either 'tls' or 'smtps'." . PHP_EOL
);
exit(1);
}
$mail = new PHPMailer(true);
try {
// Use SMTP rather than PHP's built-in mail() function.
$mail->isSMTP();
$mail->Host = $host;
$mail->Port = $port;
$mail->SMTPAuth = true;
$mail->Username = $username;
$mail->Password = $password;
$mail->CharSet = PHPMailer::CHARSET_UTF8;
// Select the encryption mode that matches your Volanea SMTP settings.
$mail->SMTPSecure = $encryption === 'smtps'
? PHPMailer::ENCRYPTION_SMTPS
: PHPMailer::ENCRYPTION_STARTTLS;
// Keep certificate verification enabled in every real environment.
$mail->SMTPAutoTLS = true;
// Envelope and message headers.
$mail->setFrom($fromAddress, $fromName);
$mail->addAddress($toAddress, $toName);
$mail->addReplyTo('support@example.com', 'Example App Support');
// Transactional content. Keep a plain-text alternative for recipients
// whose clients do not render HTML email.
$mail->isHTML(true);
$mail->Subject = 'Welcome to Example App';
$mail->Body = <<<HTML
<!doctype html>
<html lang="en">
<body>
<h1>Welcome!</h1>
<p>Your account is ready to use.</p>
<p>If you did not create this account, contact support.</p>
</body>
</html>
HTML;
$mail->AltBody = "Welcome!\n\nYour account is ready to use.\n\nIf you did not create this account, contact support.";
$mail->send();
echo "Email accepted by the SMTP relay for processing." . PHP_EOL;
exit(0);
} catch (Exception $exception) {
fwrite(STDERR, "Email was not sent: {$mail->ErrorInfo}" . PHP_EOL);
exit(1);
}
The support@example.com reply-to address is also a placeholder. Replace it with a real mailbox on a domain you control, or remove the addReplyTo() line if replies should go to the From address.
Run the example
From the project directory, run:
php send-email.php
If the SMTP relay accepts the message, the script prints:
Email accepted by the SMTP relay for processing.
Then check the recipient inbox and spam folder. If the message is accepted but does not appear promptly, inspect your Volanea delivery activity and the receiving mailbox’s filters before repeatedly resending the same test.
How the PHP code works
The most important decision in the sample is $mail->isSMTP(). PHP’s built-in mail() function delegates delivery to the server’s local mail transport and gives your application little direct control over the outbound relay. Using SMTP makes the relay explicit, authenticates with the credentials assigned to your account, and applies the chosen TLS mode.
$mail->SMTPAuth = true enables SMTP authentication. The username and password must be supplied exactly as assigned. Avoid trimming credentials manually beyond normal environment-value handling, adding quotes to copied values, or substituting a REST API key for an SMTP password unless your current Volanea configuration specifically identifies it as the SMTP credential.
The code uses PHPMailer::ENCRYPTION_STARTTLS when VOLANEA_SMTP_ENCRYPTION=tls. STARTTLS upgrades the connection to TLS before authentication and message submission. It uses PHPMailer::ENCRYPTION_SMTPS when VOLANEA_SMTP_ENCRYPTION=smtps, which begins with encrypted SMTP. These are separate connection styles; a port and encryption mismatch commonly produces a timeout, TLS handshake error, or connection that closes unexpectedly.
Why send both HTML and text content
Body contains the HTML version, while AltBody contains the plain-text version. Sending both is better than sending HTML only because some mail clients, security tools, accessibility workflows, and automated systems prefer or require a readable text alternative.
Keep the two versions semantically aligned. The HTML version can include headings, buttons, and branded layout, but the plain-text version should still tell the recipient what happened, why they received the message, and what action they can take.
Separate transport acceptance from delivery
The script reports that the relay accepted the message; it does not claim the message was delivered. SMTP submission happens before delivery to the recipient’s server, and delivery happens before an inbox provider chooses where to place the message.
This distinction matters operationally. Treat a successful call as proof that your application handed off the message, then use delivery events and logs to understand bounces, deferrals, complaints, or downstream delivery status. Do not make business-critical decisions, such as marking an email address as verified, solely because an SMTP client reported submission success.
Use the example safely in a web application
The CLI script is ideal for validating configuration, but production applications usually send email from a request handler, job worker, scheduled command, or queue consumer. The connection code stays mostly the same; what changes is when your application calls it and how it handles failures.
Do not place SMTP credentials in browser JavaScript, mobile applications, public repositories, or client-rendered environment variables. A user who can inspect the client must never be able to retrieve a sending credential.
Send from server-side code only
A browser contact form should submit to your server. The server validates input, applies rate limits and abuse controls, decides whether an email should be sent, and then uses the SMTP integration. This prevents visitors from directly using your sending credentials or turning the form into an open relay.
For password resets, verification emails, receipts, login alerts, and invitations, store enough application state to safely reconstruct or audit the message. For example, store a hashed password-reset token with an expiration time rather than sending a reusable secret that your database cannot validate.
Put sends on a queue when the workflow allows it
SMTP is network I/O. A recipient server delay, DNS issue, TLS negotiation problem, or provider-side throttling can make a send take longer than a web request should. For non-immediate messages, queue a job and let a worker send it outside the user-facing request.
Queues also give you a controlled place to retry transient failures. A retry should be bounded and deliberate. Retrying every error can amplify an outage, create duplicates, or keep sending to a recipient address that is permanently invalid.
A practical retry policy distinguishes between categories:
- Transient transport failures may warrant a small number of retries with exponential backoff.
- Authentication failures should not be retried until credentials or permissions are fixed.
- Permanent recipient failures should be handled through bounce and suppression processes rather than immediate resubmission.
- Application validation errors should be fixed before a job enters the sending pipeline.
Avoid duplicate transactional mail
If your application retries after a network timeout, it may not know whether the relay accepted the original submission. The safe approach is to create an application-level idempotency strategy.
For example, generate a unique event ID for an order receipt, store it with a status such as pending, submitted, or failed, and prevent another worker from sending the same event unless a human or explicit recovery policy authorizes it. This protects recipients from multiple receipts or multiple password-reset emails triggered by one event.
Common errors
The errors below are common when sending email with PHP over SMTP. Start with the exact error text, then confirm the host, port, encryption mode, username, password, sender, and network policy one at a time.
Authentication failed or SMTP authentication error
An authentication error usually means the username, password, authentication method, or SMTP endpoint does not match the account configuration. It can also occur if a credential was rotated, revoked, copied with an extra character, or used in the wrong environment.
Check the following:
- Confirm that you are using SMTP credentials, not automatically assuming a REST API key is valid for SMTP authentication.
- Re-copy the username and password from the current account setup information without adding surrounding quotes.
- Verify that the SMTP host belongs to the same Volanea account and environment as the credential.
- Check whether the credential was revoked or rotated after your
.envfile or deployment secret was created. - Restart long-running PHP workers after changing environment variables; a queue worker may retain old values in memory.
Do not log the password while troubleshooting. Log only non-secret details such as the hostname, port, encryption mode, and the first few characters of an already-safe credential identifier if your organization permits it.
Connection timed out or could not connect to SMTP host
A timeout occurs before authentication. The host may be incorrect, outbound SMTP may be blocked by your hosting provider, a firewall may deny the chosen port, DNS may fail, or the encryption mode may not match the endpoint.
First, verify the host and port against your current SMTP settings. Next, test outbound connectivity from the same server or container that runs PHP. Local development working does not prove that a production container, serverless runtime, shared host, or corporate network permits outbound SMTP traffic.
Some environments restrict outbound connections on common SMTP ports to reduce abuse. If that is the case, work with the hosting provider or network administrator rather than weakening TLS settings or trying random ports.
TLS handshake failed, certificate verify failed, or connection closed
A TLS error often indicates that tls and smtps have been paired with the wrong port or host. For example, an endpoint expecting STARTTLS will not necessarily work as an implicit TLS endpoint, and vice versa.
Use the exact encryption configuration assigned to your account. Keep certificate verification enabled. Disabling certificate verification may appear to make a development test work, but it weakens transport security and can hide the real configuration problem.
Also check system time. A server with a badly incorrect clock can fail certificate validation because certificates have defined validity periods.
Sender rejected, from address rejected, or relay access denied
A sender rejection usually points to the MAIL_FROM_ADDRESS value, not the PHP syntax. The From address may belong to an unverified domain, a domain not configured for the selected account, or an address that violates a sender policy.
Use an address on a sending domain you have configured. Keep the visible From domain aligned with your authenticated sending domain whenever possible. Do not use arbitrary customer addresses in the From field; use a controlled sender and put the customer’s address in Reply-To when the workflow requires a reply path.
Message accepted but not visible in the inbox
Check spam, quarantine, secondary inbox tabs, and mailbox filtering first. Then inspect the message headers if it arrives, or consult delivery and event data if it does not.
A mailbox provider can filter a message even when SMTP submission succeeded. Domain authentication, sender reputation, recipient engagement patterns, message content, link destinations, and complaint history can all affect placement. Sending the same test many times to the same inbox is not a reliable deliverability benchmark.
PHP cannot find vendor/autoload.php
This means Composer dependencies are not installed in the directory where the script expects them, or the script is running from a different deployment artifact.
Run composer install --no-dev --optimize-autoloader as part of production builds, ensure the vendor directory is included in the deployed artifact when appropriate, and keep this line aligned with the actual project structure:
require __DIR__ . '/vendor/autoload.php';
Environment variable is missing
If the script prints Missing required environment variable, verify the .env file is in the same directory as send-email.php for local testing. Confirm the variable name exactly matches the code and that there are no invisible characters around the equals sign or value.
In production, .env may not be loaded at all—and that is often correct. Define the variable in the runtime environment and make sure the PHP process or worker has been restarted after the deployment changed.
Wrong Content-Type or broken HTML
SMTP itself carries a MIME message, and PHPMailer sets the required multipart content types when isHTML(true) and AltBody are used. Avoid manually overriding Content-Type headers unless you understand MIME boundaries and encoding.
If an email displays raw HTML tags, confirm that $mail->isHTML(true) is set before sending and that you assigned HTML to Body. If an email lacks a readable fallback, ensure AltBody is not empty.
Async or await mistakes
PHP does not use JavaScript’s async and await syntax in this example. If you are integrating PHP with a JavaScript frontend, do not attempt to call SMTP from the browser or paste JavaScript await code into a PHP handler.
The browser should call your authenticated server endpoint. PHP should perform the mail send server-side, either during a controlled request or in a job worker. If you later use an asynchronous PHP framework, follow that framework’s mail and queue conventions rather than assuming its API matches JavaScript promises.
Production hardening checklist
Before relying on a transactional message flow in production, review the following checklist:
- Use an authenticated domain and a From address you control.
- Store SMTP credentials in a server-side secret manager or protected environment configuration.
- Keep
.envfiles, logs, and error reports free of credentials and recipient-sensitive content. - Use TLS with certificate verification enabled.
- Send both HTML and plain-text content.
- Validate recipient addresses at the application boundary and consider using an email address verification tool before adding addresses to high-value workflows.
- Rate-limit public forms and require authentication or abuse controls where appropriate.
- Queue non-immediate sends and use bounded retry behavior.
- Add idempotency controls for receipts, invites, and other events that must not duplicate.
- Observe bounces, complaints, deliveries, and failures rather than treating submission success as final delivery.
These controls are not merely operational polish. They reduce duplicate messages, help protect sender reputation, make incidents easier to diagnose, and provide a clearer audit trail when a customer says they did not receive an email.
Next steps
Once the basic PHP send works, move beyond a one-off test message.
Add webhooks for delivery visibility
Webhooks let your application receive event notifications after submission, such as delivery outcomes, bounces, complaints, or other message lifecycle events exposed by your account. Use them to update application records, suppress repeatedly failing recipients, and distinguish a sent message from one that was actually delivered.
A webhook endpoint should verify the provider’s signature according to the current webhook documentation, respond quickly with a successful HTTP response after validation, and hand expensive work to a queue. Store event identifiers and make processing idempotent because webhook providers can retry delivery.
Move repeatable content into templates
Templates help keep common transactional messages consistent across applications and teams. A welcome email, password reset, receipt, alert, or invitation can share a tested layout while your PHP code supplies the recipient-specific values.
Keep template variables narrowly scoped, validate required values before sending, and render a preview for realistic test data. Treat template changes as production changes: review them, test the plain-text fallback, check every link, and confirm that unsubscribe or preference requirements are appropriate for the message type.
Add message-level observability
Record an internal event ID, recipient identifier, message purpose, and submission timestamp for each send. Do not store more message content or personal data than your privacy and retention policies require.
When an issue occurs, this information makes it possible to connect an application event—such as password_reset_requested or invoice_paid—to the sending attempt and later delivery events. That is far more useful than searching server logs for a generic “mail sent” line.
FAQ
Can I use PHP’s built-in mail() function instead?
You can, but this guide uses SMTP because it explicitly connects to Volanea with authenticated credentials and controlled TLS settings. mail() depends on a local server mail transport, which is often unavailable or inconsistently configured in modern hosting environments.
Should I use a REST API key as my SMTP password?
Not unless your current Volanea SMTP setup specifically states that the API key is the SMTP password. REST API credentials and SMTP credentials can be different. Use the host, port, encryption mode, username, and password assigned for SMTP.
Which SMTP encryption setting should I choose?
Use the setting provided for your account. In this guide, tls means STARTTLS and smtps means implicit TLS. Do not choose based solely on a commonly used port number.
Does “accepted by the SMTP relay” mean the message reached the inbox?
No. It means the SMTP relay accepted the message for processing. Final delivery and inbox placement occur later and can be affected by recipient-server responses, suppression status, domain authentication, reputation, and mailbox filtering.
Can I send attachments with this PHP setup?
Yes. PHPMailer supports attachments, but add them only when necessary and validate file size, MIME type, access control, and content before attaching. For example, use $mail->addAttachment('/safe/path/invoice.pdf', 'invoice.pdf'); after confirming the file is safe and readable.