LettermintLettermint
  • Knowledge base
  • Community
  • Changelog
  • Support
  • Documentation
  • Sending API
  • Team API
  • MCP server
Get started
Send email
    Send with
      SDKs
      Frameworks
        LaravelSymfonyNuxtMagento 2WordPress
    SMTP
    Email activitySchedulingTest emailsTLSIdempotencySuppressionsTagsInline imagesData retentionSending limits
    Tracking
Receive email
Manage
Resources
Frameworks

Symfony

Use the official Lettermint Symfony Mailer package to send email through the Sending API. The transport supports Symfony email messages, attachments, tags, metadata, and queued mail. See SDKs and integrations for other packages.

Requirements

Before you start, you need:

  • PHP 8.2 or newer, with the PHP version required by your Symfony release
  • Symfony Mailer 6.4, 7.4, or 8.x and Composer
  • A Lettermint account with a verified sending domain
  • A Project API token from your project settings

1. Installation

Install the package with Composer:

TerminalCode
composer require lettermint/symfony-mailer

2. Configuration

Register the transport

Add the transport factory to the existing services section:

Code
services: Lettermint\SymfonyMailer\Transport\LettermintTransportFactory: autoconfigure: false arguments: $dispatcher: '@?event_dispatcher' $client: '@http_client' $logger: '@?logger' tags: ['mailer.transport_factory']

You must register this factory. Package installation alone does not register the Lettermint transport with Symfony Mailer.

Set the Project API token

Add the DSN to .env.local. Replace PROJECT_TOKEN with your Project API token:

Code
MAILER_DSN=lettermint+api://PROJECT_TOKEN@default

URL-encode the token if it contains reserved URL characters. Keep the host as default.

Configure Symfony Mailer to use this DSN:

Code
framework: mailer: dsn: '%env(MAILER_DSN)%'

3. Send your first email

Create a service that receives MailerInterface. This example uses the standard Symfony service configuration with autowiring enabled:

Code
<?php namespace App\Service; use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Email; final class WelcomeEmail { public function __construct(private MailerInterface $mailer) { } public function send(string $recipient): void { $email = (new Email()) ->from(new Address('hello@yourdomain.com', 'Example')) ->to($recipient) ->subject('Welcome to Example') ->text('Your account is ready.') ->html('<h1>Welcome!</h1><p>Your account is ready.</p>'); $this->mailer->send($email); } }

Replace the sender with an address on your verified domain. Inject WelcomeEmail into a controller or service, then call send('recipient@example.com').

4. Email options

The examples below modify a Symfony Email object named $email. Add these options before you call $mailer->send($email).

Recipients and reply-to

Code
$email ->to('customer@example.com') ->cc('manager@yourdomain.com') ->bcc('archive@yourdomain.com') ->replyTo('support@yourdomain.com');

The transport uses Symfony's envelope recipients and respects recipient overrides. At least one To recipient must remain in the envelope. CC-only and BCC-only envelopes are not supported.

Attachments

Code
$email->attachFromPath('/path/to/invoice.pdf', 'invoice.pdf', 'application/pdf');

The transport also supports inline images and stream attachments. It encodes attachment content for the API.

Route and delivery settings

Add default options to the DSN:

Code
MAILER_DSN="lettermint+api://PROJECT_TOKEN@default?route=transactional&timeout=15&track_opens=false&track_clicks=true&tls=enforced"
  • route selects a route by its slug.
  • timeout sets the request timeout in seconds. Use a positive number. The default is 15 seconds.
  • track_opens and track_clicks accept true or false.
  • tls accepts opportunistic or enforced. It controls TLS for email delivery.

Use OptionsHeader to change options for one email:

Code
use Lettermint\SymfonyMailer\Header\OptionsHeader; $email->getHeaders()->add(new OptionsHeader([ 'route' => 'transactional', 'settings' => [ 'track_opens' => false, 'tls' => 'enforced', ], 'tags' => [ ['name' => 'category', 'value' => 'receipt'], ], ]));

Per-email settings override the matching DSN defaults. Other default settings stay active. The transport removes this header from the delivered email.

To schedule an email, add scheduled_at to the same options array. Use a future timestamp with an explicit time zone, such as (new \DateTimeImmutable('+1 hour'))->format(\DateTimeInterface::ATOM).

Tags and metadata

Use OptionsHeader for named tags, as shown above. For a single tag and metadata, use Symfony's headers:

Code
use Symfony\Component\Mailer\Header\MetadataHeader; use Symfony\Component\Mailer\Header\TagHeader; $email->getHeaders()->add(new TagHeader('receipt')); $email->getHeaders()->add(new MetadataHeader('order_id', '12345')); $email->getHeaders()->add(new MetadataHeader('customer_id', 'cust_789'));

Each MetadataHeader adds one string value to the API metadata. If you add multiple TagHeader objects, only the last tag is used. See tags for details.

Idempotency

Set an explicit key to prevent duplicate emails when your application repeats a send:

Code
$email->getHeaders()->addTextHeader('Idempotency-Key', 'receipt-12345');

Use the same key for each retry of the same email. The transport sends the key as an HTTP header and removes it from the delivered email. It does not generate keys or retry requests automatically. See idempotency for the API behavior.

5. Queued mail

If your application sends email through Symfony Messenger, set all options and the idempotency key before you queue the message. The transport preserves these headers when Symfony serializes the email.

Run the Messenger worker for the transport configured in your application. With queued mail, the worker sends the API request and handles send failures.

6. Responses and errors

MailerInterface::send() returns no value. In a Symfony application, use SentMessageEvent to read the Lettermint message ID:

Code
<?php namespace App\EventListener; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\Attribute\AsEventListener; use Symfony\Component\Mailer\Event\SentMessageEvent; #[AsEventListener] final class EmailSentListener { public function __construct(private LoggerInterface $logger) { } public function __invoke(SentMessageEvent $event): void { $this->logger->info('Email accepted by the mail transport.', [ 'message_id' => $event->getMessage()->getMessageId(), ]); } }

This listener requires Symfony service autoconfiguration. For the Lettermint transport, getMessageId() returns the API's message_id. API acceptance does not confirm delivery. Use webhooks for delivery and bounce events.

API failures throw Lettermint\SymfonyMailer\Transport\ApiException. It extends Symfony's TransportException and exposes statusCode and responseBody. Network failures throw TransportException. Catch TransportExceptionInterface to handle both types in a synchronous send. For queued mail, configure failure handling in Messenger.

Use Symfony Mailer without the Symfony framework

Register the factory in a transport registry. Set LETTERMINT_PROJECT_TOKEN in your environment before you run this script:

Code
<?php require_once 'vendor/autoload.php'; use Lettermint\SymfonyMailer\Transport\LettermintTransportFactory; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Mailer\Transport; use Symfony\Component\Mime\Email; $projectToken = getenv('LETTERMINT_PROJECT_TOKEN'); if ($projectToken === false || $projectToken === '') { throw new \RuntimeException('Set LETTERMINT_PROJECT_TOKEN before sending email.'); } $registry = new Transport([new LettermintTransportFactory()]); $transport = $registry->fromString( 'lettermint+api://'.rawurlencode($projectToken).'@default' ); $mailer = new Mailer($transport); $email = (new Email()) ->from('hello@yourdomain.com') ->to('recipient@example.com') ->subject('Hello from Lettermint') ->text('This is a test email.'); $mailer->send($email);

Use this registry to resolve the Lettermint DSN. Symfony's static Transport::fromDsn() method does not discover this package.

Next steps

Tracking

Track email opens and link clicks.

Webhooks

Receive delivery and bounce events.

Test addresses

Test delivery and bounce handling.

PHP SDK

Send email with the PHP SDK.

GitHub repository

Read the source code or report an issue.

LaravelNuxt
On this page
  • Requirements
  • 1. Installation
  • 2. Configuration
    • Register the transport
    • Set the Project API token
  • 3. Send your first email
  • 4. Email options
    • Recipients and reply-to
    • Attachments
    • Route and delivery settings
    • Tags and metadata
    • Idempotency
  • 5. Queued mail
  • 6. Responses and errors
  • Use Symfony Mailer without the Symfony framework
  • Next steps
YAML
YAML
PHP
PHP
PHP
PHP
PHP
PHP
PHP
PHP