Why Clerk default email routing hurts B2B SaaS onboarding
Clerk is the authentication standard for modern Next.js and React applications. It handles complex session tokens, organization memberships, and multi-factor authentication seamlessly. However, out of the box, Clerk routes all verification codes, magic links, and password resets through a shared delivery pool using clerk.email.
For consumer side-projects, shared delivery is acceptable. For B2B SaaS applications, it creates immediate onboarding friction. Mailbox providers like Google Workspace and Microsoft 365 inspect the envelope sender domain. When your application operates on app.yourdomain.com but auth emails arrive from a shared clerk.email domain, corporate spam filters frequently flag, quarantine, or delay the message by 5 to 15 minutes.
By connecting SadaSend as Clerk’s custom SMTP provider, every authentication message originates from your verified custom domain, cryptographically signed with Dual-DKIM (2048-bit RSA + Ed25519) and strict SPF alignment.
| Delivery Metric | Default Clerk Pool | SadaSend Custom SMTP |
|---|---|---|
| Envelope Sender | Shared clerk.email address | auth@yourdomain.com (100% Branded) |
| DKIM Cryptography | Shared Clerk public key | Dual-DKIM (RSA-2048 + Ed25519) |
| DMARC Alignment | Partial / Mismatched envelope | Strict 100% Domain Alignment (p=reject) |
| Submission Latency | Queued via shared cloud pool | Sub-150ms Port 587 STARTTLS direct MX |
| Bounce Protection | Penalizes sending reputation | In-flight syntax & MX shield before queue |
1. Provisioning a dedicated ESMTP submission key in SadaSend
To connect Clerk to SadaSend, you need an SMTP submission credential. In your SadaSend Dashboard, navigate to App -> Keys and generate an API key with the smtp:send permission.
SadaSend provides a standardized RFC 5321 submission relay on Port 587 with opportunistic STARTTLS encryption:
# SadaSend ESMTP Connection Parameters
SMTP Host: smtp.sadasend.com
SMTP Port: 587
Encryption: STARTTLS (RFC 3207)
Username: api
Password: sada_sec_live_your_api_key_hereThis credential is tied strictly to your account quotas and domain allowlist, preventing unintended egress while enabling full-speed delivery.
2. Configuring Custom SMTP in the Clerk Dashboard
With your credentials ready, open your Clerk Dashboard and follow these steps:
1. In the left navigation, select Configure -> Email, SMS, & Push.
2. Scroll down to the Custom Email Provider section and select Custom SMTP.
3. Populate the configuration fields using your SadaSend relay settings:
Host: smtp.sadasend.com
Port: 587
Require TLS: Checked (STARTTLS enabled)
Username: api
Password: sada_sec_live_...
From Address: auth@yourdomain.com
From Name: Your Company NameClick "Save and test". Clerk will transmit a test verification email over Port 587. Inspect your SadaSend Dashboard (/app/logs) to verify the live 250 2.0.0 OK acceptance log with sub-150ms latency.
3. Verifying DNS records: Dual-DKIM, SPF, and DMARC alignment
To ensure your Clerk verification emails never land in the spam folder, publish three standard DNS records on your domain registrar (Cloudflare, Vercel, Route 53, or Namecheap):
; 1. SPF Authentication Record (TXT)
yourdomain.com. IN TXT "v=spf1 include:spf.sadasend.com ~all"
; 2. DKIM Public Key (TXT, value from your dashboard)
sada1._domainkey.yourdomain.com. IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkq…"
; 3. DMARC Enforcement Policy (TXT)
_dmarc.yourdomain.com. IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc@yourdomain.com; pct=100"Once published, click "Verify DNS" in SadaSend (/app/domains). The state will transition to verified in under 15 seconds, unlocking Dual-DKIM signing across all outbound Clerk emails.
4. Automating personalized welcome sequences with Clerk Webhooks & React Email
While Clerk handles 6-digit OTP codes and password resets over custom SMTP, you can use Clerk Svix webhooks to trigger personalized onboarding flows when new users finish signup.
Here is a Next.js 15 Route Handler that listens to the user.created webhook and dispatches a branded React Email template using the SadaSend SDK:
// app/api/webhooks/clerk/route.ts
import { Webhook } from 'svix';
import { headers } from 'next/headers';
import { SadaSend } from 'sadasend';
const sadasend = new SadaSend({ apiKey: process.env.SADASEND_API_KEY! });
export async function POST(req: Request) {
const headerPayload = await headers();
const svixId = headerPayload.get('svix-id');
const svixTimestamp = headerPayload.get('svix-timestamp');
const svixSignature = headerPayload.get('svix-signature');
if (!svixId || !svixTimestamp || !svixSignature) {
return new Response('Missing Svix headers', { status: 400 });
}
const payload = await req.json();
const body = JSON.stringify(payload);
const wh = new Webhook(process.env.CLERK_WEBHOOK_SECRET!);
let evt: { type: string; data: { email_addresses: Array<{ email_address: string }>; first_name?: string } };
try {
evt = wh.verify(body, {
'svix-id': svixId,
'svix-timestamp': svixTimestamp,
'svix-signature': svixSignature,
}) as typeof evt;
} catch {
return new Response('Invalid webhook signature', { status: 400 });
}
if (evt.type === 'user.created') {
const primaryEmail = evt.data.email_addresses[0]?.email_address;
const firstName = evt.data.first_name || 'there';
if (primaryEmail) {
// Sub-150ms welcome dispatch with pre-send bounce shield
await sadasend.emails.send({
from: 'founder@yourdomain.com',
to: primaryEmail,
subject: `Welcome to the platform, ${firstName}!`,
html: `<p>Hi ${firstName}, your account is officially set up. Explore the docs to get started.</p>`,
verifyBeforeSend: true,
});
}
}
return new Response('Webhook received', { status: 200 });
}Frequently Asked Questions
Send transactional emails in under 3 minutes
EU-hosted delivery, custom SMTP relay, 100% SPF/DKIM alignment, and automated suppression firewalls. 6,000 free sends every month without a credit card.
Building SadaSend — transactional email with an MCP server that has a ceiling. Writes about deliverability, email infrastructure, and what happens when you hand an autonomous agent a sending credential.