Email System
Transactional emails via Zoho ZeptoMail.
Overview
Sistine Starter uses the Zoho ZeptoMail HTTPS API for transactional emails. All email logic is in lib/email.ts; newsletter subscription state remains in the local database.
Email Types
| Trigger | Template | |
|---|---|---|
| Verification | User registration | Inline HTML in lib/email.ts |
| Password Reset | Forgot password request | Inline HTML in lib/email.ts |
| Welcome | After email verification | Inline |
| Purchase Confirmation | Payment webhook | Inline |
| Subscription Expiry | Days before expiry | Inline |
| Low Credits Alert | Balance below threshold | Inline |
Configuration
ZEPTOMAIL_SEND_MAIL_TOKEN="your-zeptomail-send-mail-token"
ZEPTOMAIL_BASE_URL="https://api.zeptomail.com.cn/v1.1"
ZEPTOMAIL_SENDER_EMAIL="Your App <noreply@mail.yourdomain.com>"Development Mode
ZeptoMail does not provide a development fallback sender. Verify a sending domain/Mail Agent and use its sender address in every environment; keep the Send Mail Token server-only.
Production
For production, add the DNS records supplied by ZeptoMail, verify the sending domain, create a Mail Agent, and set ZEPTOMAIL_SENDER_EMAIL to a sender on that verified domain.
Email Templates
The active auth and notification messages use inline HTML in lib/email.ts. The transport also accepts React elements via sendEmail({ react }) and renders them before calling ZeptoMail; reusable components in the emails/ directory can be used by explicit callers:
// emails/verification-email.tsx
import { Html, Body, Text, Link } from '@react-email/components';
export default function VerificationEmail({ url }: { url: string }) {
return (
<Html>
<Body>
<Text>Click below to verify your email:</Text>
<Link href={url}>Verify Email</Link>
</Body>
</Html>
);
}Error Handling
All email sends are wrapped in try-catch. Failed sends are logged but don't crash the application. The system returns a { success, data, error } result instead of throwing:
const result = await sendVerificationEmail(email, token);
if (!result.success) {
// Handle the provider failure without exposing credentials.
}