Skip to main content
The mailchannels/mailchannels-php package ships a Symfony Mailer transport. If you already have a Symfony application that builds Symfony\Component\Mime\Email objects and sends them through MailerInterface, you can switch to MailChannels with just a few lines of configuration.
This plugin is optional and lives alongside the rest of the PHP SDK. The plugin itself can only send mail. It doesn’t implement the full API surface (sub-accounts, keys, webhooks, etc.). For those features, use the SDK’s Client directly.

Prerequisites

Before you send your first message, follow the PHP quickstart prerequisite section. This will walk you through creating an account, generating an API key, and adding the required Domain Lockdown and SPF DNS records.

Installation

Install the SDK and the Symfony Mailer component:
The SDK also needs a PSR-18 HTTP client and PSR-17 factories. Symfony HTTP Client + Nyholm PSR-7 is a natural fit for a Symfony project (Guzzle works too, but install one or the other, not both):

Configuring Symfony

1

Register the transport factory

Symfony discovers mailer transports through tagged services. Register the factory in config/services.yaml:
config/services.yaml
See Symfony’s docs on custom transport factories for background on how this mechanism works.
2

Point MAILER_DSN at MailChannels

Set the MAILER_DSN environment variable, in your .env file or your environment:
.env
  • Scheme: mailchannels or mailchannels+api (both accepted).
  • DSN user: your MailChannels API key.
  • DSN host: leave as default for the production API endpoint.
See Symfony’s transport setup docs for more on the DSN format and how it’s typically wired up per-environment.

Sending mail

Once configured, send mail through the standard MailerInterface. No MailChannels-specific code is required for a basic send:
See Symfony’s docs on creating and sending messages for everything else Email supports (multiple recipients, reply-to, attachments, and so on).
This transport only builds one personalization per message. If you need to send multiple individualized emails in one request, call the SDK’s Client directly instead of going through MailerInterface. See How emails are structured for more information on personalizations.

Control headers

Symfony’s Email object has no equivalent for some MailChannels API features. Reach those instead through custom headers prefixed with x-mailchannels-, set on the Email’s header bag. The x-mailchannels-* headers are stripped before the message is sent, so recipients never see them.
If the same custom header is added twice, only the last value is used.
Boolean headers accept only the literal strings "true" / "false"; anything else throws Symfony\Component\Mailer\Exception\TransportException. An empty value is treated as unset (falls back to the default). Each of these corresponds directly to a field on the send endpoint. See that reference for full semantics.
This integration only supports DKIM via the x-mailchannels-dkim-* headers above.If a DKIM-Signature header is already present on the message, the message is rejected, since the provided signature won’t match the message the API constructs.See DKIM signing and key management for information on how MailChannels handles signing and keys.

Reading the result

  • Synchronous send (x-mailchannels-send-async: false): the sent message’s ID is written back onto Symfony’s SentMessage object, so you can read it after sending.
  • Queued (default): the API doesn’t return a message ID per call, so Symfony’s own generated message ID is left untouched on SentMessage.

Error handling

Any SDK exception raised while sending (authentication, validation, rate limiting, server errors) is caught and re-thrown as Symfony\Component\Mailer\Exception\TransportException. Access the original exception via $e->getPrevious() to inspect it:

Limitations

  • One personalization per message. No per-recipient template data or per-recipient header overrides through this transport.
  • No Mustache templating. This transport has no way to use mailchannels’ mustache template rendering. If you need to do so, use Symfony’s Template Support, or use the SDK’s Client directly; see Templates for how.
  • DKIM only via the MailChannels API, as described above.

Webhooks

The plugin also has optional support for parsing webhook events into Symfony’s RemoteEvent objects.

Installation

Install the Symfony Webhook and RemoteEvent components, as well as ext-sodium for webhook signature verification:
While the Symfony mailer plugin requires version 6.4 or higher, the webhook plugin requires Symfony 7.2 or higher, as previous versions do not have support for batched webhook processing.

Configuring Symfony

1

Register the webhook service

Register the webhook parser in config/services.yaml:
config/services.yaml
2

Add the webhook route

Register the webhook route in config/webhooks.yaml:
config/webhooks.yaml
See the Symfony docs on webhook endpoints for more information on how this works.
You will now have a webhook route available at /webhook/mailchannels.

Consuming RemoteEvents

When a webhook is received, it will automatically be parsed into a Symfony\Component\RemoteEvent\RemoteEvent object and passed to your consumer.
src/RemoteEvent/MailChannelsWebhookConsumer.php
Almost all events will automatically be parsed into Symfony’s MailerDeliveryEvent or MailerEngagementEvent, with additional information about the type of event and the message it relates to. See the Symfony RemoteEvent docs for more information on how to consume events. The MailChannels webhook integration adds one extra RemoteEvent type: MailChannels\Plugins\Symfony\RemoteEvent\TestEvent. This will be used when you send a test webhook from the MailChannels dashboard or API. You can use this to verify that your webhook endpoint is working correctly.

Next steps

  • Read the webhooks guide to learn more about real-time notifications for email delivery, bounces, and complaints.
  • Explore the API reference to see what else you can do with the API.