Gemini Omni Editor Docs
Email

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

EmailTriggerTemplate
VerificationUser registrationInline HTML in lib/email.ts
Password ResetForgot password requestInline HTML in lib/email.ts
WelcomeAfter email verificationInline
Purchase ConfirmationPayment webhookInline
Subscription ExpiryDays before expiryInline
Low Credits AlertBalance below thresholdInline

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.
}

On this page