Search by

somehow-digital / craft-lettermint

thasmo

Lettermint Mailer Adapter for Craft CMS.

Package info

github.com/somehow-digital/craft-lettermint

Homepage

Documentation

Type:craft-plugin

pkg:composer/somehow-digital/craft-lettermint

Statistics

Installs: 2 592

Dependents: 0

Suggesters: 0

Stars: 4

Open Issues: 4

v2.0.0 2026-10-25 22:21 UTC

This package is auto-updated.

Last update: 2026-11-08 21:27:37 UTC


README

Lettermint icon

Lettermint for Craft CMS

Lettermint integration for Craft CMS.

Latest version Tests Craft CMS 5 PHP 8.2+ License

Features Β· Installation Β· Setup Β· Pro Edition Β· Support

Features

Feature Standard Pro
πŸ“¬ Mail transport with route and tags βœ… βœ…
🌐 Route and tags per site βœ…
🏷️ Craft metadata on every email βœ…
πŸ“Š Utility with usage, statistics and checks βœ…
🧩 Dashboard widget βœ…
πŸ”” Webhooks as Craft events βœ…
🩺 Health endpoint for uptime monitors βœ…
πŸ–₯️ Health command for deployments βœ…

Requirements

  • Craft CMS 5.8.0 or later
  • PHP 8.2 or later
  • A Lettermint account with a verified domain

Installation

Install the plugin from the Plugin Store in the Control Panel, or with Composer:

composer require somehow-digital/craft-lettermint
php craft plugin/install lettermint

Setup

Three steps and your emails go out through Lettermint:

  1. Go to Settings β†’ Email.
  2. Set Transport Type to Lettermint.
  3. Fill in the settings below and click Save.
Setting Required Description
Project API Token βœ… Found on your projects page under β€œAPI Token”.
Route Slug A custom route. Leave blank for the default transactional route.
Tags Tags for all emails, e.g. environment=production, campaign=welcome. Use only letters, digits, _ and -.

Important

The System Email Address must use a domain you set up in Lettermint.

Tip

All settings support environment variables.

Pro Edition

Pro adds per-site settings, insights in the Control Panel, webhooks and health checks. Its settings appear below the standard settings β€” all of them are optional, so turn on only what you need.

Setting What it does
Team API Token Unlocks the Team API features. Team owners create it in the dashboard under Manage team β†’ β€œAPI Tokens”.
Project ID of the project your Project API Token belongs to. Needed for project details, statistics, domains and checks.
Webhook Secret Turns on the webhook endpoint.
Health Token Turns on the health endpoint.
Sites Sets a route and tags per site.

Note

Lettermint can't tell which project a token belongs to, so set Project yourself.

🌐 Per-site routes and tags β€” send each site through its own route, with its own tags

Β 

Set a Route Slug and Tags for each site in the Sites table. Both support environment variables.

The site comes from the message (e.g. a user's affiliated site) or the current request.

  • Route Slug β€” the site's route replaces the adapter's route.
  • Tags β€” the site's tags are merged with the adapter's tags. Tags with the same name are replaced.

Example: adapter environment=production, site=default + site site=shop β†’ environment=production, site=shop

Route Slug Tags
Site value set Replaces the adapter's route Merged with the adapter's tags
Site value empty Adapter's route is used Adapter's tags are used
X-LM-Options header on the message Takes precedence Replaces site and adapter tags
Limit β€” Max. 20 tags in total

🏷️ Craft metadata β€” see which site, user and system message an email belongs to

Β 

Every email is sent with Craft context as Lettermint metadata. You can see it with each message in Lettermint.

Key Value
craft_email Key of the system message, e.g. forgot_password
craft_site Handle of the site
craft_user ID of the recipient user, e.g. with setTo($user)
craft_language Language of the email, e.g. de-AT
craft_environment Craft environment, e.g. production

Empty keys are skipped. For example, craft_email is missing for emails that aren't system messages.

Add your own metadata β€” metadata set on the message wins over the Craft metadata:

use Symfony\Component\Mailer\Header\MetadataHeader;

$message->getSymfonyEmail()->getHeaders()->add(new MetadataHeader('order_id', '1234'));

πŸ“Š Utility β€” your team, usage, statistics and domains at a glance

Β 

Go to Utilities β†’ Lettermint for an overview of your team and project.

Section Shows
Team Name, plan and verification
Usage Emails sent this billing period vs. your plan
Tokens Whether Lettermint accepts your API tokens
Project Creation date, sandbox mode, SMTP, hidden email content, default route with open and click tracking (as far as Lettermint tells), token last used, and a link to the project in Lettermint
Health Overall status and checks, with a link to the health endpoint for admins
Emails Sent, delivered, hard bounces, spam complaints, opens and clicks over 28 days, with a chart
Domains Verification status of the project's domains
Routes Slug, type and default route of the project, with open and click tracking (as far as Lettermint tells)
Settings One row for all sites and one per site with the System Email Address, sender name, reply-to address, HTML email template, route and tags, and whether webhooks and the health endpoint are on β€” never tokens or secrets

βœ”οΈ Project checks make sure that:

  • the project is live,
  • the System Email Address domain β€” also of each site that overrides it β€” belongs to the project and is verified,
  • all route slugs (adapter and sites) are outgoing routes of the project.

πŸ’‘ Good to know

  • Only the configured project is loaded, never other projects of the team.
  • The Project API Token status works without a Team API Token.
  • Sections the token can't read are hidden and show a notice.
  • The page never waits for Lettermint: it loads its data in the background. Data is fresh for 5 minutes and shared with the dashboard widget. Older data is shown right away while it's reloaded, and before anything is loaded, the page shows its layout as a placeholder. Data is only cached when Lettermint could be reached, so an outage doesn't stick around.
  • Click Refresh to reload the data. Saving other settings reloads it as well.
  • Access requires the Utilities β†’ Lettermint permission.

🧩 Dashboard widget β€” a compact overview right on your dashboard

Β 

Add the Lettermint widget to your dashboard. It's a compact version of the utility and shows:

  • team and plan,
  • usage this billing period,
  • sent, delivered, hard bounces and spam complaints over 28 days, with a daily chart.

It shares its data with the utility. Only users with the Utilities β†’ Lettermint permission can add or see it.

πŸ”” Webhooks β€” react to deliveries, bounces and complaints with Craft events

Β 

Receive Lettermint webhook events (deliveries, bounces, complaints, …) as Craft events.

Setup

  1. In the Lettermint dashboard, create a webhook and choose its events. Use the endpoint URL shown below the Webhook Secret setting, e.g. https://example.com/actions/lettermint/webhook.
  2. Copy its signing secret (whsec_…) into Webhook Secret.

Usage

use SomehowDigital\Craft\Lettermint\services\WebhooksService;
use SomehowDigital\Craft\Lettermint\events\WebhookEvent;
use yii\base\Event;

Event::on(WebhooksService::class, WebhooksService::EVENT_RECEIVE_WEBHOOK, function (WebhookEvent $event) {
    if ($event->type === WebhooksService::TYPE_MESSAGE_HARD_BOUNCED) {
        $event->payload->id;   // delivery ID
        $event->payload->data; // event data, e.g. `recipient`
    }
});

Every event type has a TYPE_* constant, e.g. TYPE_MESSAGE_DELIVERED or TYPE_SUPPRESSION_ADDED. Test deliveries from the dashboard use TYPE_WEBHOOK_TEST. New types from Lettermint fire the event too.

[!TIP]

  • The same delivery can arrive more than once. Use $event->payload->id to skip duplicates β€” it stays the same across retries.
  • Keep handlers fast. Push slow work to the queue.
  • $event->payload is a SomehowDigital\Craft\Lettermint\models\WebhookPayload with the id, event, timestamp and data of the delivery.

Responses and retries

Situation Response Lettermint retries?
Valid signature, handlers succeed 200 β€”
Invalid signature or older than 5 minutes 401 No
A handler throws an exception 500 Yes, for up to ~14 hours
No response within 30 seconds β€” Yes

Webhooks are also received while the system is offline.

🩺 Health endpoint β€” let your uptime monitor know if emails can be sent

Β 

Check if emails can be sent, e.g. from an uptime monitor.

Setup

  1. Generate a token with at least 16 characters and no spaces, e.g. openssl rand -hex 32.
  2. Store it in an environment variable, e.g. LETTERMINT_HEALTH_TOKEN.
  3. Set Health Token to $LETTERMINT_HEALTH_TOKEN.

[!WARNING] Don't enter the token directly. It would be saved in the project config and end up in version control.

Usage

curl -H "Authorization: Bearer $LETTERMINT_HEALTH_TOKEN" https://example.com/actions/lettermint/health

The overall status is the worst check result: ok, warning or error.

Status Response
ok or warning 200
error 503
Missing/wrong token 401
Endpoint turned off 404

The endpoint is off without a Health Token or in the Standard edition. Results are cached for 60 seconds to stay within Lettermint's rate limits. The endpoint also works while the system is offline.

Example response
{
    "status": "warning",
    "date": "2026-11-05T10:00:00+00:00",
    "checks": [
        { "id": "token", "status": "ok", "text": "Lettermint accepts the project API token." },
        { "id": "teamToken", "status": "ok", "text": "Lettermint accepts the team API token." },
        { "id": "usage", "status": "warning", "text": "The monthly volume of the plan is nearly used up, with 46000 of 50000 emails." },
        { "id": "delivery", "status": "ok", "text": "The project is live." },
        { "id": "sender", "status": "ok", "text": "The domain β€œexample.com” of the system email address belongs to the project." },
        { "id": "route", "status": "ok", "text": "Emails use the default route β€œoutgoing” of the project." }
    ],
    "usage": { "used": 46000, "included": 50000, "share": 0.92 }
}

date shows when the checks ran. Match on the check id, not the text β€” the text is translated.

All checks
Check Needs Fails with
token β€” error: token rejected or Lettermint unreachable
teamToken Team API Token warning: token rejected or team unreadable
usage Team API Token warning: 90% or more of the monthly volume used
project Team API Token warning: project not in the team or couldn't be loaded
delivery Team API Token + Project warning: project in sandbox mode
sender, sender.<site> Team API Token + Project error: domain not in the project, warning: not verified
route, route.<site> Team API Token + Project error: not an outgoing route of the project
Can't send headers, or getting 401?
  • If your monitor can't send headers, use ?key=… instead. Prefer the header when possible β€” query strings end up in server logs.
  • Some Apache setups drop the Authorization header before it reaches PHP. If you get 401 with the right token, pass the header through or use ?key=….

πŸ–₯️ Health command β€” run the health checks during a deployment

Β 

Run the same checks as the health endpoint from the command line. No Health Token needed, and results are never cached.

php craft lettermint/health
ok      token      Lettermint accepts the project API token.
ok      teamToken  Lettermint accepts the team API token.
warning usage      The monthly volume of the plan is nearly used up, with 46000 of 50000 emails.

Status: warning
Option Description
--json Print the report as JSON, like the endpoint
--strict Fail on warning too
Exit code When
0 ok or warning
69 error, or warning with --strict
78 Lettermint isn't the transport, or Standard edition

Support

License

Released under the MIT License.

somehow digital.