Firebase email with Groundmail
Send Firebase email from your own domain through Groundmail. There are three ways, and you can use more than one:
| What you're sending | Use | Needs code |
|---|---|---|
| Firebase Auth emails (verification, password reset, email change) | Firebase Auth SMTP settings | No |
| Email when a Firestore document is written | The Trigger Email extension | A little |
| Any email from your backend | A Cloud Function with the Node SDK | Yes |
Prerequisites
- A Groundmail account with a verified sending domain. A subdomain such as
mail.yourdomain.comworks well. - For the SMTP paths, an SMTP credential: in the Groundmail dashboard, open SMTP and choose Create SMTP credential. The password (
gmsmtp_…) is shown once, so copy it. See SMTP relay. - For Cloud Functions, an API key (
gm_…) from API keys in the dashboard. - The Trigger Email extension and Cloud Functions need Firebase's Blaze (pay as you go) plan. The Auth SMTP settings don't.
1. Firebase Auth emails: SMTP settings
By default Firebase sends Auth emails from noreply@<project-id>.firebaseapp.com. To send them from your domain, open the Firebase console, go to Authentication → Templates → SMTP settings, turn SMTP on and enter:
| Firebase field | Value |
|---|---|
| Sender address | An address on your verified domain, for example no-reply@mail.yourdomain.com |
| SMTP server host | smtp.groundmail.dev |
| SMTP server port | 587 |
| SMTP account username | groundmail |
| SMTP account password | Your SMTP credential (gmsmtp_…) |
| SMTP security mode | STARTTLS |
Save. Firebase now sends verification, password reset and email change messages through Groundmail, using the templates you set in the same Templates tab.
2. Firestore: the Trigger Email extension
The Trigger Email extension (firebase/firestore-send-email) sends an email whenever a document is added to a collection. Install it from the Firebase console or the CLI:
firebase ext:install firebase/firestore-send-emailWhen it asks for configuration:
| Extension setting | Value |
|---|---|
| SMTP connection URI | smtp://groundmail@smtp.groundmail.dev:587 |
| SMTP password | Your SMTP credential (gmsmtp_…), stored in Secret Manager |
| Email documents collection | mail (or your own name) |
| Default FROM address | An address on your verified domain |
Then send by adding a document:
import { addDoc, collection } from "firebase/firestore";
await addDoc(collection(db, "mail"), {
to: "ada@example.com",
message: {
subject: "Welcome to Acme",
html: "<p>Thanks for signing up.</p>",
},
});The extension writes the result back to the document's delivery field.
3. Cloud Functions: the Node SDK
For full control over templates and sending, call the API from a function with the groundmail package:
cd functions
npm install groundmail
firebase functions:secrets:set GROUNDMAIL_API_KEY// functions/src/index.ts
import { onCall } from "firebase-functions/v2/https";
import { defineSecret } from "firebase-functions/params";
import { Groundmail } from "groundmail";
const GROUNDMAIL_API_KEY = defineSecret("GROUNDMAIL_API_KEY");
export const sendWelcome = onCall({ secrets: [GROUNDMAIL_API_KEY] }, async (req) => {
const groundmail = new Groundmail(GROUNDMAIL_API_KEY.value());
await groundmail.emails.send({
from: "Acme <no-reply@mail.acme.com>",
to: req.auth!.token.email!,
subject: "Welcome to Acme",
html: "<p>Thanks for signing up.</p>",
});
});To send Firebase password reset links from your own template, generate the link with the Firebase Admin SDK (getAuth().generatePasswordResetLink(email)) and put it in the email you send with emails.send.
4. Send a test
Send to a Groundmail simulator address so nothing reaches a real inbox, for example by adding a mail document with to: "delivered@simulator.groundmail.dev". Then open Emails in the Groundmail dashboard, or run:
curl "https://api.groundmail.dev/emails?limit=5" \
-H "Authorization: Bearer $GROUNDMAIL_API_KEY"The email appears with "test": true and the status delivered. All simulator outcomes are in Testing.
Troubleshooting
Firebase or the extension can't connect. The username is exactly groundmail, and the password is the SMTP credential (gmsmtp_…), not an API key (gm_…). Use port 587 with STARTTLS.
The message is refused because of the sender. The sender or FROM address must be on a verified domain on your account.
A recipient never receives the email. The address may be on your suppression list, usually after a hard bounce. See Suppressions.
You hit a sending limit. New accounts ramp up their daily volume, and the Free plan has a daily limit. See Sending limits.