Send email from your customer's domain
White-label transactional email
In white-label SaaS, your customers want auth emails to come from THEIR domain, not yours.
Customer: Acme Corp. End user: alice@bigcorp.com.
Without:
From: noreply@your-saas.com
Subject: Reset your password
"Click to reset for Acme Corp..."User sees @your-saas.com. Confusing, they expected acme.com email.
With:
From: noreply@acme.com
Subject: Reset your Acme account passwordUser sees their company's domain. Consistent.
Implementation
Step 1: Customer adds DNS
Customer's IT admin adds DNS records:
acme.com SPF: v=spf1 include:_spf.your-ESP.com -all
acme.com DKIM: TXT record with your ESP's key
acme.com DMARC: v=DMARC1; p=quarantine; rua=mailto:dmarc@acme.comOR for full per-customer isolation:
mail.acme.com → CNAME to your ESP's tracking domainStep 2: Verify domain
async function verifyDomain(customerId, domain) {
// Step 1: ask ESP to provision the domain
const ddi = await sendgrid.domain.create({ domain });
// Step 2: customer adds DNS records
// ...
// Step 3: verify
const verified = await sendgrid.domain.verify(ddi.id);
if (!verified) return { error: "dns_not_propagated" };
// Step 4: mark verified for customer
await db.update("customer_email_domains").set({ verified_at: new Date() }).where({ id: ddi.id });
}Step 3: Send from customer's domain
async function sendVerificationEmail(user, customer) {
const fromDomain = customer.email_domain ?? "your-saas.com"; // fallback
await sendgrid.send({
from: `noreply@${fromDomain}`,
to: user.email,
subject: `Reset your ${customer.name} account password`,
text: `Hi, click here to reset: ${url}`,
});
}ESP options
Most transactional ESPs support per-customer domains:
- SendGrid Inbound Parse / Domain Authentication.
- Postmark Sender Signatures + Domains.
- AWS SES Configuration Sets.
- Resend Domains.
Each has their own provisioning flow.
Per-domain templates
Branding in emails:
From: noreply@acme.com
Subject: Reset your password
Hi Alice,
Click below to reset your Acme password:
{{ url }}
The Acme teamCustomer's branding throughout. Template per customer? OR shared template with customer-specific variables.
Variables:
customer:
name: "Acme"
primary_color: "#FF5733"
logo_url: "https://acme.com/logo.png"
support_email: "support@acme.com"Inject into template:
"Hi, {{ user.first_name }}, this is from {{ customer.name }}..."Reply-to
Even from noreply@acme.com, replies might come back to your-saas.com. Avoid:
await sendgrid.send({
from: `noreply@${customerDomain}`,
replyTo: customer.support_email,
...
});User replies → goes to acme.com's support, not yours.
Bounce handling
When email to alice@example.com bounces, the bounce goes back to the From domain.
Option A: bounces handled at customer's domain (their problem). Option B: bounces routed to your-saas.com via Return-Path.
Most ESPs handle Return-Path automatically, bounces come to ESP for tracking, both you and customer see metrics.
DKIM rotation
DKIM keys rotate occasionally. ESP handles, customer DNS records reference key by selector:
selector1._domainkey.acme.com → CNAME → ...
selector2._domainkey.acme.com → CNAME → ...Two selectors. When one rotates, fallback. No service interruption.
Spam considerations
Sending from customer's domain has trade-offs:
Pros:
- User trusts the domain.
- Anti-phishing: hard for attacker to spoof acme.com if SPF/DKIM/DMARC right.
Cons:
- Customer's sender reputation affects deliverability. Their cold list = your problem.
- DMARC misconfig blocks delivery silently.
Educate customers on email best practices.
Fallback to your domain
If customer's domain isn't verified yet:
const fromDomain = customer.email_domain_verified ? customer.email_domain : "your-saas.com";Send from yours. Once they verify, switch.
Tracking
Add a tracking pixel? Differentiated per customer:
<img src="https://track.your-saas.com/open?customer=acme&email=alice@example.com" />Or use ESP's built-in tracking (most embed automatically).
Privacy: disclose tracking in customer's privacy policy.
Cost
Some ESPs charge per domain:
- SendGrid: included.
- Postmark: 1 sender signature per account; need per-customer servers.
- Resend: included.
Pick ESP that fits your white-label model.
Multi-tenant courier
Kratos's courier supports configurable per-flow templates and senders. Override at runtime:
# Webhook from Kratos calls your service to enrich
courier:
channels:
- id: email
type: http
request_config:
url: http://your-mailer:3000/sendYour mailer service:
app.post("/send", async (req, res) => {
const { recipient, subject, body, identity_id } = req.body;
const customer = await getCustomerForIdentity(identity_id);
await sendgrid.send({
from: `noreply@${customer.email_domain}`,
to: recipient,
subject,
text: body,
});
res.json({ ok: true });
});Per-recipient customer lookup; right domain.