Developer guideIntegrations
Firebase Auth with SMTP or the API
Firebase Auth emails (verification and password reset) delivered by Couryo, over SMTP in the Auth settings, through the Firestore email extension or by generating the link in your backend.
Three paths, from the simplest to the most flexible. Before any of them: verify the domain in Couryo and create an API key with the send scope.
| Path | Good when |
|---|---|
| 1. SMTP in the Auth settings | you want to keep Firebase's templates and only change who delivers |
| 2. Firestore email extension | your app already writes documents and you want to send product emails that way |
| 3. Generate the link in your backend | you want the whole email your way (HTML, language, brand, idempotency) |
1. SMTP in the Auth settings#
In the Firebase console, under Authentication > Templates, open SMTP settings, enable it and fill in:
| Field | Value |
|---|---|
| Sender address | an address on the verified domain, such as no-reply@example.com |
| SMTP server host | smtp.couryo.com |
| Port | 587 |
| Username | couryo |
| Password | your API key (ck_live_...) |
| SMTP security mode | STARTTLS (or SSL with port 465) |
Verification, password reset and email change messages now go out through Couryo, each with its timeline in the dashboard. Edit the texts under Templates: put your app's name in the subject and body.
2. "Trigger Email from Firestore" extension#
Install the official Firestore email extension and, in its configuration:
| Parameter | Value |
|---|---|
| SMTP connection URI | smtps://couryo@smtp.couryo.com:465 |
| SMTP password | your API key (stored as the extension's secret) |
| Email documents collection | mail |
| Default FROM address | Example <no-reply@example.com> |
To send, write a document:
import { addDoc, collection, getFirestore } from "firebase/firestore";
await addDoc(collection(getFirestore(), "mail"), {
to: ["ana@example.com"],
message: {
subject: "Your order has shipped",
text: "Hi Ana, order 1042 has shipped.",
html: "<p>Hi Ana, order <strong>1042</strong> has shipped.</p>",
},
});The extension updates the document with the result (delivery.state). Protect the collection in your Firestore rules: only your backend should be able to write to it.
3. Generate the link in your backend and send through the API#
The Admin SDK generates the same link Firebase would send. You send it with your own HTML, in the person's language, with an idempotency key:
import { getAuth } from "firebase-admin/auth";
import { Couryo } from "@couryo/node";
const couryo = new Couryo({ language: "en" }); // COURYO_API_KEY
export async function sendVerification(uid: string) {
const user = await getAuth().getUser(uid);
const link = await getAuth().generateEmailVerificationLink(user.email!, { url: "https://example.com/sign-in" });
await couryo.emails.send(
{
from: "Example <no-reply@example.com>",
to: user.email!,
subject: "Confirm your email for Example",
html: `<p>Hi! Confirm this address is yours to sign in to Example.</p><p><a href="${link}">Confirm email</a></p>`,
text: `Hi! Confirm this address is yours to sign in to Example: ${link}`,
},
{ idempotencyKey: `verification-${uid}` },
);
}For passwords, use generatePasswordResetLink(email). Call the function from a user-created trigger or from your own route.
Common problems#
| Symptom | What to do |
|---|---|
| Nothing arrives and no error in the app | look for the send under Emails in the Couryo dashboard; if it is not there, Firebase could not hand it to SMTP (check host, port and password) |
550 5.7.1 |
the sender is not on a verified domain, or the account is still on level 0 |
| "Invalid or expired" link | the link is single-use: a corporate filter may have opened it first. Generate a new one and, if it keeps happening with one domain, use path 3 with an intermediate confirmation screen |
| Goes to spam | see Why emails go to spam |