Transactional Emails
The Gully Store application uses Zeptomail to send automated transactional emails. This comprehensive guide covers setup, configuration, implementation, and troubleshooting.
Quick Start (5 Minutes)
1. Add Environment Variables
Add to your .env file:
ZEPTOMAIL_API_KEY=your_api_key_here
ZEPTOMAIL_FROM_EMAIL=noreply@yourdomain.com
ZEPTOMAIL_FROM_NAME=Gully Store
2. Restart Development Server
npm run dev
3. Test the Implementation
# Test welcome email
curl -X POST http://localhost:3000/api/test/email \
-H "Content-Type: application/json" \
-d '{"type":"welcome","email":"your@email.com","name":"Test User"}'
# Test order confirmation
curl -X POST http://localhost:3000/api/test/email \
-H "Content-Type: application/json" \
-d '{"type":"order","email":"your@email.com","amount":99.99}'
4. Verify Success
Check your server logs for:
📧 [Zeptomail] Sending WELCOME email...
✅ [Zeptomail] Email sent successfully
Overview
Email Types
Transactional emails are automatically sent for:
- Welcome Email - New user signup
- Order Confirmation - Successful payment/checkout
Key Features
✅ Non-blocking - Never delays user experience
✅ Automatic triggers - No manual intervention needed
✅ Error handling - Graceful failures with logging
✅ No dependencies - Uses native fetch API
✅ Test endpoint - Easy manual testing
Complete Setup Guide
Prerequisites
- Zeptomail account (sign up here)
- Verified sender domain
- Zeptomail API key
Step 1: Get Your Zeptomail API Key
- Log in to your Zeptomail account
- Navigate to Settings → API Keys
- Create a new API key or copy existing one
- Keep this key secure
Step 2: Verify Your Domain
- In Zeptomail, go to Mail Agents → Add Mail Agent
- Add your domain (e.g.,
yourdomain.com) - Add required DNS records:
- SPF record
- DKIM record
- CNAME records
- Wait for verification (up to 48 hours)
Step 3: Configure Templates
The implementation uses template aliases instead of template IDs for consistency across test and production environments. Aliases remain the same while key IDs may differ between environments.
Two templates should be configured in your Zeptomail account:
Template 1: Welcome Email
- Template Alias:
welcome-email-html - Purpose: Sent to new users on signup
Template 2: Order Confirmation
- Template Alias:
order-confirmation-html - Purpose: Sent after successful payment
Template files are available in /email-templates/ directory for reference.
Step 4: Set Environment Variables
Add to .env:
# Zeptomail Configuration
ZEPTOMAIL_API_KEY=your_api_key_here
ZEPTOMAIL_FROM_EMAIL=noreply@yourdomain.com # Must be from verified domain
ZEPTOMAIL_FROM_NAME=Gully Store
Step 5: Verify Setup
Restart your application and test both email types.
Email Templates
Welcome Email
Template Alias: welcome-email-html
Trigger: New user signup via Supabase authentication
Variables:
user_name- User's display name (defaults to "there" if not provided)company_name- Set to "Gully Store"
Content Includes:
- Welcome message
- Company introduction
- Getting started information
- Contact information
Order Confirmation
Template Alias: order-confirmation-html
Trigger: Successful payment processing (Square/Intuit/PayPal)
Variables:
user_name- Customer's name (defaults to "Valued Customer")order_id- Internal order/payment ID (for support reference)transaction_id- Payment processor transaction IDorder_date- Formatted date (e.g., "October 13, 2025")items_table_rows- HTML table rows with item details (HTML template only)items_list_text- Plain text item list (text template only)subtotal- Subtotal amount before taxtax_amount- Tax amount chargedtotal_amount- Total amount charged (including tax)currency_symbol- Currency symbol ($ for USD)company_name- Company name "Gully Sports" (text template footer)support_email- Support email address
Content Includes:
- Order confirmation message
- Order details and receipt
- Transaction information
- Itemized purchase list
- Support contact
Implementation Details
Email Client Module
Location: /src/utils/zeptomailClient.ts
Core Functions:
// Generic templated email function
sendTemplateEmail(params: {
to: { email: string; name?: string };
templateAlias: string;
mergeData: Record<string, any>;
replyTo?: { email: string; name?: string };
})
// Welcome email convenience function
sendWelcomeEmail(email: string, name?: string)
// Order confirmation convenience function
sendOrderConfirmationEmail(params: {
email: string;
name?: string;
orderId: string;
transactionId: string;
amount: number;
currency?: string;
productName?: string;
items?: Array<{name: string; quantity: number; price: number}>;
orderDate?: Date;
})
Integration Points
1. User Signup (Welcome Email)
Location: /src/utils/userSync.ts
Flow:
- User completes signup form
- Supabase creates authentication account
syncUserWithDatabase()creates user record in CockroachDBsendWelcomeEmail()is called automatically (non-blocking)- Email is sent to user's email address
Implementation:
// Create new user in CockroachDB
user = await prisma.user.create({
data: {
supabaseId: supabaseUserId,
email,
name: name || null,
avatarUrl: avatarUrl || null,
role: "USER",
},
});
// Send welcome email (non-blocking)
sendWelcomeEmail(email, name).catch((error) => {
console.error("⚠️ Failed to send welcome email:", error);
// User creation still succeeds
});
2. Payment Processing (Order Confirmation)
Locations:
/src/app/api/payments/submit/route.ts(main handler)/src/app/api/payments/square/submit/route.ts(Square-specific)
Flow:
- User completes checkout
- Payment is processed and verified
- Payment record saved to database
sendOrderConfirmationEmail()called with order details (non-blocking)- Email sent to user's email address
Implementation:
// Store payment in database
const payment = await prisma.payment.create({
data: {
userId: user.id,
amount: paymentAmount,
provider: "SQUARE",
transactionId: result.transactionId!,
status: "COMPLETED",
},
});
// Send order confirmation (non-blocking)
if (userEmail) {
sendOrderConfirmationEmail({
email: userEmail,
name: userName,
orderId: payment.id,
transactionId: result.transactionId!,
amount: paymentAmount,
currency: "USD",
productName: productName,
items: items,
orderDate: new Date(),
}).catch((error) => {
console.error("⚠️ Failed to send order confirmation email:", error);
});
}
Non-Blocking Architecture
All email operations are non-blocking by design:
sendWelcomeEmail(email, name).catch((error) => {
console.error("⚠️ Failed to send welcome email:", error);
// Operation continues - email failure doesn't block user flow
});
Benefits:
- User signup completes even if email service is down
- Payment processing completes even if email fails
- Better user experience with faster response times
- Email failures are logged for monitoring
- Application remains reliable
Testing
Test Endpoint
Location: /src/app/api/test/email/route.ts
GET Request: Returns usage instructions
POST Request: Sends test emails
Welcome Email Test
curl -X POST http://localhost:3000/api/test/email \
-H "Content-Type: application/json" \
-d '{"type":"welcome","email":"test@example.com","name":"Test User"}'
Order Confirmation Test
curl -X POST http://localhost:3000/api/test/email \
-H "Content-Type: application/json" \
-d '{
"type":"order",
"email":"test@example.com",
"amount":99.99,
"name":"Test Customer"
}'
Manual Testing
Test Welcome Email
- Sign up a new user account on your application
- Check server logs for success messages
- Check email inbox (and spam folder)
- Verify email content and formatting
Test Order Confirmation
- Add items to cart
- Complete checkout with test payment credentials
- Check server logs for success messages
- Check email inbox (and spam folder)
- Verify order details are correct
Verify Success
Look for these log messages:
📧 [Zeptomail] Sending WELCOME email to user@example.com...
✅ [Zeptomail] Email sent successfully to user@example.com
Monitoring
Success Indicators
📧 [Zeptomail] Sending {template} email to {email}...
✅ [Zeptomail] Email sent successfully to {email}
Error Indicators
❌ [Zeptomail] ZEPTOMAIL_API_KEY is not configured
❌ [Zeptomail] Invalid template key: {key}
❌ [Zeptomail] API Error: {details}
⚠️ Failed to send {type} email: {error}
Zeptomail Dashboard
Monitor email delivery in your Zeptomail account:
- Go to Reports → Email Reports
- View delivery status, opens, clicks
- Check bounce and complaint rates
- Monitor API usage and quotas
- Check for failed deliveries
Troubleshooting
Email Not Sending
Symptoms: No email received, error logs show API errors
Solutions:
- Verify
ZEPTOMAIL_API_KEYis set in.env - Check API key is valid in Zeptomail dashboard
- Ensure sender domain is verified
- Review DNS records (SPF, DKIM)
- Test API key with curl:
curl -X POST https://api.zeptomail.com/v1.1/email/template \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"template_alias": "welcome-email-html",
"to": [{"email_address": {"address": "test@example.com"}}],
"from": {"address": "noreply@yourdomain.com"},
"merge_info": {"user_name": "Test"}
}'
Template Variables Not Rendering
Symptoms: Email shows placeholder text instead of values
Solutions:
- Check variable names match exactly (case-sensitive)
- Verify template in Zeptomail uses correct syntax
- Ensure
mergeDataobject has all required variables - Test template in Zeptomail dashboard with sample data
From Address Rejected
Symptoms: "Sender not verified" or authentication errors
Solutions:
- Verify domain ownership in Zeptomail
- Ensure DNS records (SPF, DKIM) are properly configured
- Wait for DNS propagation (up to 48 hours)
- Use exact email address configured in Zeptomail
Rate Limiting
Symptoms: Some emails fail with rate limit errors
Solutions:
- Check your Zeptomail plan limits
- Implement email queuing for high-volume scenarios
- Consider upgrading your Zeptomail plan
- Use batch sending API for multiple recipients
Emails Going to Spam
Symptoms: Emails delivered but in spam folder
Solutions:
- Verify SPF and DKIM records are properly configured
- Check sender reputation in Zeptomail dashboard
- Avoid spam trigger words in email content
- Ensure proper from address configuration
- Request users whitelist your sender address
API Reference
sendTemplateEmail(params)
Generic function to send any templated email.
Parameters:
{
to: {
email: string; // Recipient email address
name?: string; // Optional recipient name
};
templateAlias: string; // Template alias (e.g., "welcome-email-html")
mergeData: Record<string, any>; // Template variables
replyTo?: {
email: string; // Reply-to email
name?: string; // Reply-to name
};
}
Returns: Promise<{ success: boolean; data?: any }>
Example:
import { sendTemplateEmail } from "@/utils/zeptomailClient";
const result = await sendTemplateEmail({
to: {
email: "user@example.com",
name: "John Doe",
},
templateAlias: "welcome-email-html",
mergeData: {
user_name: "John Doe",
company_name: "Gully Store",
},
replyTo: {
email: "support@yourdomain.com",
name: "Support Team",
},
});
sendWelcomeEmail(email, name?)
Convenience function to send welcome emails to new users.
Parameters:
email: string- Recipient email addressname?: string- Optional recipient name (defaults to "there")
Returns: Promise<{ success: boolean; data?: any }>
Example:
import { sendWelcomeEmail } from "@/utils/zeptomailClient";
await sendWelcomeEmail("newuser@example.com", "John Doe");
sendOrderConfirmationEmail(params)
Convenience function to send order confirmation emails after payment.
Parameters:
{
email: string; // Customer email
name?: string; // Customer name
orderId: string; // Internal order ID
transactionId: string; // Payment transaction ID
amount: number; // Total amount charged
currency?: string; // Currency code (default: "USD")
productName?: string; // Single product name (legacy)
items?: Array<{ // List of purchased items
name: string;
quantity: number;
price: number;
}>;
orderDate?: Date; // Order date (default: now)
}
Returns: Promise<{ success: boolean; data?: any }>
Example:
import { sendOrderConfirmationEmail } from "@/utils/zeptomailClient";
await sendOrderConfirmationEmail({
email: "customer@example.com",
name: "Jane Smith",
orderId: "ord_abc123",
transactionId: "txn_xyz789",
amount: 149.99,
currency: "USD",
items: [
{ name: "Item 1", quantity: 2, price: 50.0 },
{ name: "Item 2", quantity: 1, price: 49.99 },
],
orderDate: new Date(),
});
Security Best Practices
API Key Management
- Never commit API keys to version control
- Use environment variables for all sensitive data
- Rotate API keys regularly (quarterly recommended)
- Monitor email logs for suspicious activity
- Restrict API key permissions to minimum required
Email Security
- Validate email addresses before sending
- Implement rate limiting on signup/checkout endpoints
- Use HTTPS for all API communications
- Monitor for abuse (excessive signups, spam)
- Protect user data in email content
Data Privacy
- Email addresses only used for transactional emails
- No marketing emails without explicit consent
- Comply with GDPR, CAN-SPAM, and similar regulations
- Users can manage preferences in account settings
- Email content doesn't expose sensitive data
Performance
Email Sending Performance
- Average send time: < 1 second
- Non-blocking: No user-facing delays
- Zeptomail SLA: 99.9% uptime
- No new dependencies: Uses native fetch API
Scalability
Current Implementation:
- Free tier: 10,000 emails/month
- Paid plans: Higher volumes available
- Concurrent sends: Multiple emails can be sent simultaneously
Future Enhancements:
- Email queue for high-volume scenarios
- Retry logic for failed sends
- Batch sending for multiple recipients
- Background workers for processing
Implementation Summary
Files Modified
/src/utils/userSync.ts- Added welcome email sending/src/app/api/payments/submit/route.ts- Added order confirmation/src/app/api/payments/square/submit/route.ts- Added order confirmation for Square
Files Created
/src/utils/zeptomailClient.ts- Core email module/src/app/api/test/email/route.ts- Test endpoint/email-templates/*.html- Template reference files/email-templates/*.txt- Text template references
Dependencies
No new npm packages required!
Uses:
- Native
fetchAPI - Built-in TypeScript types
- Existing Next.js infrastructure
Production Checklist
Before deploying to production:
- Update
ZEPTOMAIL_FROM_EMAILto your verified domain - Verify domain in Zeptomail dashboard
- Add SPF/DKIM records to DNS
- Test welcome email with real signup
- Test order confirmation with real payment
- Check emails aren't going to spam
- Monitor Zeptomail dashboard for delivery stats
- Review email content and formatting
- Set up delivery monitoring/alerting
- Configure rate limits on endpoints
- Document support process for email issues
Future Enhancements
Potential improvements to consider:
- Email Queue: Background job queue for reliability
- Retry Logic: Automatic retries for failed sends
- Email Analytics: Track open rates and click rates
- More Templates: Password reset, booking confirmations, receipts
- Personalization: Dynamic content based on user preferences
- A/B Testing: Test different email variations
- Localization: Multi-language email support
- Email Preferences: User preference management
- Batch Sending: Send to multiple recipients efficiently
- Webhook Integration: Receive delivery status updates
Support Resources
Internal Documentation
- Implementation code:
/src/utils/zeptomailClient.ts - Test endpoint:
/src/app/api/test/email/route.ts - Template files:
/email-templates/
External Resources
Summary
The transactional email system provides:
✅ Automated welcome emails for new users
✅ Order confirmations after successful payments
✅ Non-blocking design - never delays user experience
✅ Robust error handling - graceful failures with logging
✅ Easy-to-use API with helper functions
✅ Comprehensive monitoring - detailed logging
✅ Secure implementation - environment-based config
✅ No new dependencies - uses native APIs
✅ Test endpoint - easy development testing
✅ Production ready - scalable and reliable
Ready to use! Just add environment variables and test.