# Send email from Laravel Cloud

Send your Laravel app's Mailables, notifications and queued mail through Groundmail on [Laravel Cloud](https://cloud.laravel.com). Install the Groundmail Laravel package, set two environment variables on the environment, and redeploy. Your `Mail::to()->send()` calls stay as they are.

---

## Prerequisites

- A Groundmail account with a [verified sending domain](https://docs.groundmail.dev/domains). A subdomain such as `mail.yourdomain.com` works well.
- An API key (`gm_…`) from **API keys** in the dashboard. A [scoped key](https://docs.groundmail.dev/api-keys) with only `emails:send` is enough for sending.
- A Laravel app deployed to a Laravel Cloud environment.

---

## 1. Install the package

On your machine, so `composer.json` and `composer.lock` change in the repo you deploy:

```bash
composer require groundmail/laravel
```

Add a mailer that uses the `groundmail` transport to `config/mail.php`:

```php
'mailers' => [
    // ...
    'groundmail' => [
        'transport' => 'groundmail',
    ],
],
```

Commit and push.

---

## 2. Set the environment variables

In Laravel Cloud, open the environment's settings, go to **Environment variables**, and add:

```bash
GROUNDMAIL_API_KEY=gm_your_key_here
MAIL_MAILER=groundmail
MAIL_FROM_ADDRESS=no-reply@mail.yourdomain.com
MAIL_FROM_NAME="${APP_NAME}"
```

`MAIL_FROM_ADDRESS` must be on a domain you verified in Groundmail. Then **redeploy the environment**: Laravel Cloud writes the variables to your app's `.env` at deploy time, so changes only apply to a new deployment.

To share one key across several environments, store it once in Laravel Cloud's **Secrets Manager** and link the secret to each environment instead.

Your build command can keep `php artisan optimize` or `php artisan config:cache`: the variables are in `.env` during the build, so the cached config has them.

---

## 3. Send

Nothing changes in your code:

```php
use App\Mail\WelcomeMail;
use Illuminate\Support\Facades\Mail;

Mail::to($user)->send(new WelcomeMail($user));
```

Queued Mailables and notifications (`Mail::to($user)->queue(...)`, `ShouldQueue`) send from your Laravel Cloud workers or managed queue with the same variables. To make a queued job safe to retry, set an idempotency key header on the Mailable, for example `'welcome-'.$this->user->id`. See [Tags, idempotency, and schedule](https://docs.groundmail.dev/frameworks/laravel#tags-idempotency-and-schedule).

For batch sends, domains or webhooks, call the API client directly:

```php
use Groundmail\Laravel\Facades\Groundmail;

Groundmail::client()->emails->send([
    'from' => 'no-reply@mail.yourdomain.com',
    'to' => 'user@example.com',
    'subject' => 'Your receipt',
    'html' => '<p>Thanks for your purchase.</p>',
]);
```

---

## If the environment already has an Email resource

Laravel Cloud's own **Email** resource injects its own `MAIL_MAILER` and `MAIL_FROM_*` variables. Variables you add yourself take priority over injected ones, so setting `MAIL_MAILER=groundmail` as above is enough. To stop injecting them at all, detach the Email resource from the environment's canvas, then redeploy.

---

## SMTP instead of the package

If you'd rather not add a package, use Laravel's built-in `smtp` mailer with the [Groundmail SMTP relay](https://docs.groundmail.dev/smtp). Create an SMTP credential under **SMTP** in the dashboard, then set:

```bash
MAIL_MAILER=smtp
MAIL_SCHEME=smtp
MAIL_HOST=smtp.groundmail.dev
MAIL_PORT=587
MAIL_USERNAME=groundmail
MAIL_PASSWORD=gmsmtp_your_credential
MAIL_FROM_ADDRESS=no-reply@mail.yourdomain.com
MAIL_FROM_NAME="${APP_NAME}"
```

Port `587` upgrades to TLS with STARTTLS. Keep `MAIL_SCHEME=smtp` (or unset): `smtps` means implicit TLS on port 465, which Groundmail doesn't offer. On Laravel 10 and earlier, use `MAIL_ENCRYPTION=tls` instead of `MAIL_SCHEME`. Port `2525` works the same way if `587` is ever blocked.

The package sends over HTTPS and maps Groundmail tags, idempotency keys and `scheduled_at` from headers; the relay supports the same headers (see [Custom headers](https://docs.groundmail.dev/smtp#custom-headers-api-features-over-smtp)). Either way, sends show up under **Emails** in the dashboard.

---

## Troubleshooting

| Problem | Fix |
|---------|-----|
| Mail still goes to `log` or another mailer | The environment wasn't redeployed after you set `MAIL_MAILER`, or the config was cached before the change. Redeploy. |
| `Unsupported mail transport [groundmail]` | `groundmail/laravel` isn't in the deployed `composer.lock`. Run `composer require groundmail/laravel` locally, commit and redeploy. |
| `401 Unauthorized` | `GROUNDMAIL_API_KEY` is missing or revoked. Create a new key under **API keys** and redeploy. |
| `domain is not verified` | `MAIL_FROM_ADDRESS` (or the Mailable's `from`) isn't on a domain you verified under **Domains**. |
| SMTP `535` authentication failed | The username must be `groundmail` and the password an SMTP credential (`gmsmtp_…`), not an API key. |

Related: [Laravel](https://docs.groundmail.dev/frameworks/laravel) · [PHP SDK](https://docs.groundmail.dev/sdks/php) · [SMTP relay](https://docs.groundmail.dev/smtp)
