# Craft CMS email with Groundmail

Send Craft CMS email through Groundmail with the official plugin for Craft 5. Account activation, password resets, Craft Commerce order email, form notifications and anything else your site sends with Craft's mailer go out from your own domain, over the API.

---

## Prerequisites

- A Groundmail account with a [verified sending domain](https://docs.groundmail.dev/domains).
- An API key (`gm_…`): in the dashboard, open **API keys** and create one. A send-only key is enough.
- Craft CMS 5 with PHP 8.2 or later, and shell access to run Composer.

Craft's email goes out as transactional email by default, so use it for account, order and notification email. Never send cold email.

---

## Install the plugin

In your Craft project, add the plugin's repository, install it with Composer, then install it in Craft:

```bash
composer config repositories.groundmail-craft vcs https://github.com/Groundmail/groundmail-craft
composer require groundmail/craft-plugin
php craft plugin/install groundmail
```

---

## Connect Craft

1. Add your API key to the project's `.env` file:

   ```bash
   GROUNDMAIL_API_KEY=gm_your_key
   ```

2. In the control panel, go to **Settings**, then **Email**.
3. Set **System Email Address** to an address on your verified domain, for example `noreply@yourdomain.com`, and set **Sender Name**.
4. Change **Transport Type** to **Groundmail**.
5. Set **API Key** to `$GROUNDMAIL_API_KEY`. Craft reads the key from the environment, so it stays out of your project config.
6. Leave **Email category** on **Transactional** unless the site only sends email about your product (see [Email category](#category)).
7. Click **Save**.

If `allowAdminChanges` is off in production, make the change in development and deploy the project config as usual. The key itself lives in each environment's `.env`.

---

## Test it

Groundmail's simulator addresses never reach a real inbox. Send Craft's test email to one:

```bash
php craft mailer/test --to=delivered@simulator.groundmail.dev
```

Or click **Test** on the **Email** settings page, which sends to your own address. The email shows up under **Emails** in the Groundmail dashboard. Sent to the simulator it has the status `delivered` and `"test": true`; use `bounce@simulator.groundmail.dev` to see a hard bounce. See [Testing](https://docs.groundmail.dev/testing#simulator-addresses).

---

## Email category {#category}

Groundmail sends [transactional, product and newsletter email from separate IPs](https://docs.groundmail.dev/deliverability#transactional-and-product-streams). **Email category** sets the stream for the email Craft sends:

- **Transactional** (default): account email, password resets, order and form notifications.
- **Product**: email about your product to its users, such as onboarding tips. Recipients can opt out of product email and keep getting transactional email.

A single message can set its own category with an `X-Groundmail-Category` header, and add [tags](https://docs.groundmail.dev/emails) with `X-Groundmail-Tag: name=value`:

```php
Craft::$app->getMailer()->compose()
    ->setTo($user->email)
    ->setSubject('Your order has shipped')
    ->setHtmlBody($html)
    ->addHeader('X-Groundmail-Tag', 'order=' . $order->number)
    ->send();
```

`X-Groundmail-Category: newsletter` sends an opt-in newsletter, once your account is approved for newsletters (email support@groundmail.dev). Until then those sends are refused with a `403` and nothing is sent.

---

## Craft Commerce

Commerce sends order emails through Craft's mailer, so once Groundmail is the transport they go out through it too. Each Commerce email has its own **Status Email Address** (under **Commerce**, then **System Settings**, then **Emails**). Leave it empty to send from the system email address, or set it to an address on your verified domain.

To know whether an order email was delivered or bounced, create a [webhook](https://docs.groundmail.dev/webhooks) for `email.delivered`, `email.bounced` and `email.complained`, or check **Emails** in the dashboard.

---

## Troubleshooting

**"Groundmail API keys start with gm_".** The **API Key** setting, or the environment variable it names, holds something else, such as an SMTP credential (`gmsmtp_…`). Create an API key under **API keys**.

**The email is refused because of the From address.** The system email address, and the **Status Email Address** of any Commerce email, must be on a [verified domain](https://docs.groundmail.dev/domains).

**A recipient never receives the email.** The address may be on your suppression list after a hard bounce or complaint. Sends to it return `422`. See [Suppressions](https://docs.groundmail.dev/suppressions).

**Sends are deferred.** New accounts have a daily ramp-up limit. See [Sending limits](https://docs.groundmail.dev/sending-limits).

---

## Related

- [Symfony Mailer transport](https://docs.groundmail.dev/frameworks/symfony)
- [Webhooks](https://docs.groundmail.dev/webhooks)
- [Testing](https://docs.groundmail.dev/testing)
- [Errors](https://docs.groundmail.dev/errors)
