/** * Email Templates - CRO-Optimized * * What's improved (based on email marketing best practices): * * Subject Lines: * - More specific with urgency/outcome ("Expires in 1 Hour") * - Emoji for visual attention in inbox * - Clear value proposition * * Design & Copy: * - Personal sender name ("Timo from QR Master") * - Storytelling instead of feature lists * - Trust elements (security badges, timelines) * - Clear CTAs with action verbs ("Reset My Password โ†’") * - Objection handling built-in ("Didn't request this?") * - Mobile-optimized (shorter paragraphs, more whitespace) * * How to measure if this works for YOUR audience: * 1. Track baseline metrics (open rate, click rate, conversions) * 2. A/B test old vs new templates * 3. Measure with real users (minimum 100 emails per variant) * * No guarantees - only your data will tell what works for QR Master users. */ import { Resend } from 'resend'; import nodemailer from 'nodemailer'; // Use a placeholder during build time, real key at runtime const resendKey = process.env.RESEND_API_KEY || 're_placeholder_for_build'; const resend = new Resend(resendKey); // Rate limiter for Resend Free Tier (1 email per second) let lastEmailSent = 0; const MIN_EMAIL_INTERVAL = 1000; // 1 second in milliseconds async function waitForRateLimit() { const now = Date.now(); const timeSinceLastEmail = now - lastEmailSent; if (timeSinceLastEmail < MIN_EMAIL_INTERVAL) { const waitTime = MIN_EMAIL_INTERVAL - timeSinceLastEmail; await new Promise(resolve => setTimeout(resolve, waitTime)); } lastEmailSent = Date.now(); } /** * Password Reset Email - Security focused with clear urgency */ export async function sendPasswordResetEmail(email: string, resetToken: string) { await waitForRateLimit(); const appUrl = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3050'; const resetUrl = `${appUrl}/reset-password?token=${resetToken}`; try { await resend.emails.send({ from: 'QR Master Security ', replyTo: 'support@qrmaster.net', to: email, subject: '๐Ÿ” Reset Your QR Master Password (Expires in 1 Hour)', html: ` Reset Your Password - QR Master
๐Ÿ”’ SECURITY ALERT

Password Reset Request

Someone requested to reset your password

Hi there,

We received a request to reset the password for your QR Master account. If this was you, click the button below to create a new password:

๐Ÿ” Reset My Password โ†’

โฑ๏ธ This link expires in 60 minutes

For your security, this password reset link will only work once and expires after 1 hour. If you need a new link, you can request another one anytime.

๐Ÿ”— Button not working? Copy and paste this link into your browser:

${resetUrl}

โ“ Didn't request this?

โœ… You can safely ignore this email. Your password won't be changed unless you click the button above. If you're concerned about your account security, please contact us immediately at support@qrmaster.net

QR Master

Secure QR Code Analytics & Management

ยฉ 2026 QR Master. All rights reserved.

This is an automated security email. Please do not reply.

`, }); console.log('Password reset email sent successfully to:', email); return { success: true }; } catch (error) { console.error('Error sending password reset email:', error); throw new Error('Failed to send password reset email'); } } /** * Newsletter Welcome Email - Value-focused with clear expectations */ export async function sendNewsletterWelcomeEmail(email: string) { await waitForRateLimit(); try { await resend.emails.send({ from: 'Timo from QR Master ', replyTo: 'support@qrmaster.net', to: email, subject: '๐ŸŽ‰ You\'re In! Here\'s What Happens Next (AI QR Features)', html: ` Welcome to QR Master AI Waitlist
โœจ EARLY ACCESS

You're on the List! ๐Ÿš€

Get ready for AI-powered QR codes that work smarter, not harder

Hey there! ๐Ÿ‘‹

Thanks for joining the waitlist for QR Master's AI-powered features. You're among the first to know when we launch something that'll completely change how you create and manage QR codes.

No fluff, no spam. Just a heads-up when these features go live.

๐ŸŽฏ What You Can Expect

โšก Smart QR Generation

AI analyzes your content and automatically suggests the optimal QR design, colors, and format for maximum scans. No more guessing what works.

๐Ÿ“Š Predictive Analytics

See scan forecasts, detect unusual patterns, and ask questions about your data in plain English. Your analytics become conversations, not spreadsheets.

๐Ÿค– Auto-Organization

Your QR codes automatically get categorized, tagged, and organized. Spend less time managing, more time creating.

๐ŸŽจ AI Design Studio

Generate unique, on-brand QR code designs in seconds. Just describe what you want, AI handles the rest.

โฐ When Will This Happen?

We're in active development and testing. You'll get an email the moment these features go live โ€“ no waiting, no wondering. We respect your inbox.

๐Ÿ’ก Want to Get Started Now?

Our current platform already helps teams create, track, and manage dynamic QR codes. No AI needed โ€“ just powerful tools that work today.

๐Ÿš€ Try QR Master Free โ†’

๐Ÿ’Œ Thanks for trusting us with your inbox. We'll make it worth it.

โ€” Timo ๐Ÿ‘‹
Founder, QR Master

www.qrmaster.net

ยฉ 2026 QR Master. All rights reserved.

You're receiving this because you signed up for AI feature notifications.

`, }); console.log('Newsletter welcome email sent successfully to:', email); return { success: true }; } catch (error) { console.error('Error sending newsletter welcome email:', error); throw new Error('Failed to send newsletter welcome email'); } } /** * AI Feature Launch Email - Excitement + clear CTA */ export async function sendAIFeatureLaunchEmail(email: string) { await waitForRateLimit(); try { await resend.emails.send({ from: 'Timo from QR Master ', replyTo: 'support@qrmaster.net', to: email, subject: '๐Ÿš€ They\'re Live! Your AI QR Features Are Ready', html: ` AI Features Are Live - QR Master
๐ŸŽ‰
โœจ NOW LIVE

AI Features Are Here! ๐Ÿš€

Ready to use in your dashboard

Remember when you signed up to be notified about our AI features?

Well, today's that day. ๐ŸŽฏ

We've been building, testing, and polishing these features for months. And they're finally ready for you to use. Right now. No beta, no waitlist, no BS.

โœจ What's New in Your Dashboard

โšก Smart QR Generation

AI analyzes your content and suggests optimal designs automatically. Just paste your link, we handle the optimization.

๐Ÿ“Š Predictive Analytics

Ask questions about your scan data in plain English. "Which campaign performed best last month?" Done.

๐Ÿค– Auto-Organization

QR codes automatically categorized and tagged. Your library stays organized without manual work.

๐ŸŽจ AI Design Studio

Generate custom QR designs by describing what you want. "Make it modern and blue" โ†’ Done in 3 seconds.

๐ŸŽฏ How to Get Started

  1. ๐Ÿ” Log in to your QR Master account
  2. ๐Ÿ‘€ Look for the "โœจ AI" badge on supported features
  3. โœ๏ธ Try creating a QR code โ€“ you'll see AI suggestions automatically
  4. ๐Ÿ“ˆ Check your Analytics tab for the new AI query interface
โœจ Try AI Features Now โ†’

โœ… Available on all plans โ€ข โšก No extra setup required

๐Ÿ’ฌ We're excited to see what you build with these new tools. If you have questions, ideas, or just want to share what you created โ€“ hit reply. I read every email.

โ€” Timo ๐Ÿ‘‹
Founder, QR Master

www.qrmaster.net โ€ข Dashboard โ€ข Help

ยฉ 2026 QR Master. All rights reserved.

You received this because you subscribed to AI feature launch notifications.

`, }); console.log('AI feature launch email sent successfully to:', email); return { success: true }; } catch (error) { console.error('Error sending AI feature launch email:', error); throw new Error('Failed to send AI feature launch email'); } } // --------------------------------------------------------------------------- // SMTP Transport (nodemailer) โ€” used for retention / welcome emails // --------------------------------------------------------------------------- function createSmtpTransport() { const host = process.env.SMTP_HOST || 'smtp.qrmaster.net'; const port = parseInt(process.env.SMTP_PORT || '465', 10); const user = process.env.SMTP_USER || 'timo@qrmaster.net'; const pass = process.env.SMTP_PASS?.trim(); if (!pass) { throw new Error('SMTP_PASS is missing. Configure SMTP credentials before sending welcome or retention emails.'); } return nodemailer.createTransport({ host, port, secure: port === 465, auth: { user, pass, }, }); } const appUrl = process.env.NEXT_PUBLIC_APP_URL || 'https://www.qrmaster.net'; // --------------------------------------------------------------------------- // Shared design tokens (email-safe inline styles) // --------------------------------------------------------------------------- const clr = { bg: '#F5F2EC', // warm parchment wrapper card: '#FFFFFF', header: '#0B0D14', // near-black header headerAccent: '#1A1D2E', gold: '#C8A257', // warm gold accent goldDim: '#A07E3A', text: '#1A1A1A', textSoft: '#5A5A5A', textMuted: '#909090', border: '#E8E3D8', pillBg: '#F0EDE5', ctaBg: '#0B0D14', ctaText: '#FFFFFF', }; const webFontHead = ` `; function emailShell(headExtra: string, bodyContent: string): string { return ` ${webFontHead} ${headExtra}
${bodyContent}

www.qrmaster.net  ยท  support@qrmaster.net

ยฉ 2026 QR Master. You're receiving this because you created an account.

`; } // Dot-grid SVG encoded as data URI for header backgrounds // Safe URL-encoded SVG without problematic internal quotes const dotGridPattern = `url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%221.5%22%20fill%3D%22%23C8A257%22%20fill-opacity%3D%220.18%22%2F%3E%3C%2Fsvg%3E)`; /** * Welcome Email โ€” sent immediately on signup (Day 0) */ export async function sendWelcomeEmail(email: string, name: string) { const transport = createSmtpTransport(); const createUrl = `${appUrl}/create`; const firstName = name.split(' ')[0]; const html = emailShell('', `
QR Master

Welcome,
${firstName}.

The physical world is now your canvas.

Beautiful QR Code Experience

Let's be honest: most QR codes are static, look terrible, and break the moment you change a link. We built QR Master to fix that.

Your account is fully activated. You now have the power to create beautiful, dynamic QR codes that adapt to your brand, never expire, and track every single scan (device, location, and time).

To give you the perfect start, we've loaded your account with everything you need:

3
Free Codes
90s
To Create
โˆž
No Expiry
Design your first QR code  โ†’
No credit card required. Zero commitment.

I'm thrilled to have you on board. If you have any questions, feedback, or just want to share what you've created โ€” hit reply. I'm reading every single email.

Timo

Timo

Founder, QR Master

`); await transport.sendMail({ from: 'Timo from QR Master ', replyTo: 'support@qrmaster.net', to: email, subject: 'Your QR Master account is ready', html, }); } /** * Activation Nudge โ€” sent on Day 3 if user has 0 QR codes */ export async function sendActivationNudgeEmail(email: string, name: string) { const transport = createSmtpTransport(); const createUrl = `${appUrl}/create`; const firstName = name.split(' ')[0]; const steps = [ { n: '01', label: 'Paste your URL', sub: 'or choose WiFi, vCard, Teams, and more' }, { n: '02', label: 'Customize the design', sub: 'colors, logo, frame label โ€” optional' }, { n: '03', label: 'Download & use', sub: 'PNG for screen, SVG for print' }, ]; const stepsHtml = steps.map(s => `
${s.n}
${s.label}
${s.sub}
`).join(''); const html = emailShell('', `
QR Master

You haven't made one yet,
${firstName}.

3 days since signup ยท 0 QR codes created

Your 3 free dynamic QR codes are still there. Unused.
Dynamic means: one code, update the link anytime, every scan tracked.

Here's all it takes:

${stepsHtml}
Make my first QR code  โ†’
Free forever ยท no card needed

If something stopped you from getting started, just reply. I'll help directly.

Timo

FOUNDER, QR MASTER

`); await transport.sendMail({ from: 'Timo from QR Master ', replyTo: 'support@qrmaster.net', to: email, subject: "You haven't made one yet", html, }); } /** * Upgrade Nudge โ€” sent on Day 7 if user has โ‰ฅ1 QR code and is still on FREE plan */ export async function sendUpgradeNudgeEmail(email: string, name: string, qrCount: number) { const transport = createSmtpTransport(); const pricingUrl = `${appUrl}/pricing`; const firstName = name.split(' ')[0]; const features = [ { label: 'Dynamic QR codes', free: '3', pro: '50', }, { label: 'Logo in QR code', free: 'โ€”', pro: 'Your logo, centered', }, { label: 'Brand colors', free: 'โ€”', pro: 'Any color', }, { label: 'CSV export', free: 'โœ“', pro: 'โœ“', }, ]; const rowsHtml = features.map((f, i) => ` ${f.label} ${f.free} ${f.pro} `).join(''); const html = emailShell('', `
QR Master

${qrCount} of 3 free codes used,
${firstName}.

Free plan ยท Day 7

The free plan gives you 3 dynamic QR codes. That's enough to start โ€” not enough to scale. When you hit the limit, every new campaign means replacing an old one.

Pro removes the ceiling โ€” and adds custom branding your free codes never have:

${rowsHtml}
Feature Free Pro
See Pro plan  โ†’
Your free plan stays active โ€” no pressure

If you have questions about what Pro unlocks, just reply here.

Timo

FOUNDER, QR MASTER

`); await transport.sendMail({ from: 'Timo from QR Master ', replyTo: 'support@qrmaster.net', to: email, subject: `You're ${qrCount >= 3 ? 'at' : `${3 - qrCount} away from`} the free limit`, html, }); } /** * 30-Day Nudge โ€” sent on Day 30 if user has โ‰ฅ1 QR code and is still on FREE plan */ export async function sendThirtyDayNudgeEmail(email: string, name: string, qrCount: number) { const transport = createSmtpTransport(); const pricingUrl = `${appUrl}/pricing`; const firstName = name.split(' ')[0]; const html = emailShell('', `
QR Master

A month in,
${firstName}.

Free plan ยท Day 30

You've created ${qrCount} QR code${qrCount !== 1 ? 's' : ''} in your first month. That tells me you're actually using this โ€” not just signing up to forget it.

The one thing I hear most from Pro users who switched after a few weeks: they wish they'd added their brand sooner. Every code they printed on free didn't have their logo. Every flyer had a generic black pattern instead of their colors.

Pro fixes that. Two things it unlocks that free never will:

โ—†

Your logo, inside every QR code

Upload once. Every code you make automatically carries your brand mark โ€” menus, flyers, packaging, wherever.

โ—

Brand colors โ€” not just black

Match your QR codes to your brand palette. Looks intentional. Scans the same.

Add my brand to QR codes  โ†’
Your existing codes keep working โ€” nothing breaks

If you ever want to talk through whether Pro makes sense for what you're building, just reply. Happy to help figure it out.

Timo

FOUNDER, QR MASTER

`); await transport.sendMail({ from: 'Timo from QR Master ', replyTo: 'support@qrmaster.net', to: email, subject: `${firstName}, a month of QR codes โ€” one upgrade worth making`, html, }); }