Skip to content
elon-email-templates logo

Elon AI Email Templates

elon-email-templates

Use when creating or modifying email templates for Elon AI - welcome emails, role changes, notifications, teacher digests, or QR code invitations. This project uses HTML string templates, NOT React Email components.

SKILL.md

Full skill instructions

Elon AI Email Templates

Overview

HTML string-based email templates for the Elon AI Classroom Assistant platform. This project does NOT use React Email components - all templates are pure HTML strings with TypeScript helper functions.

Important: The global react-email skill documents React Email patterns. This project uses a different approach optimized for simplicity and performance. Follow this skill for Elon AI email work.

When to Use

  • Creating new transactional email templates
  • Modifying existing templates in email-send.ts
  • Understanding the email queue architecture
  • Adding new email types to the system

Architecture

Email Flow

API/​Action → publishJob({jobType: "email:send"})
  → Upstash QStash (queue)
  → emailSendHandler()
  → Template selection (EMAIL_TEMPLATES[templateId])
  → HTML string generation
  → Resend API
  → User inbox

Key Files

FilePurpose
lib/​jobs/​handlers/​email-send.tsAll 12 email templates + handler (916 lines)
lib/​email/​constants.tsCentralized colors, styles, logo URL
lib/​email/​unsubscribe-token.tsHMAC-signed unsubscribe tokens
app/​api/​email/​unsubscribe/​route.tsUnsubscribe API endpoint
lib/​jobs/​types.tsEmailSendPayloadSchema definition
.email-previews/HTML preview files for visual testing

Template Pattern

All templates use pure HTML strings with helper functions:

// Template structure in EMAIL_TEMPLATES object
welcome: (data) => ({
  subject: "Welcome to Elon AI!",
  html: wrapInBaseTemplate({
    headerTitle: `Welcome, ${escapeHtml(data.name)}!`,
    headerSubtitle: "Your AI-powered study companion is ready",
    content: `
      <p style="${STYLES.bodyText}">
        Hey ${escapeHtml(data.name)}, welcome to Elon AI!
      </​p>
      ${createButton("Get Started", url)}
    `,
    proTip: "Your conversations are FERPA-protected.",
  }),
}),

Helper Functions

FunctionPurposeExample
wrapInBaseTemplate()Header + content + footer wrapperSee above
createButton()Primary/​secondary CTA buttonscreateButton("Click Me", url, "primary")
createInfoBox()Key-value info cardscreateInfoBox([{label: "Name", value: "John"}])
escapeHtml()XSS protection for user contentescapeHtml(user.name)

Style Constants

All styles are defined in STYLES object at top of email-send.ts:

const STYLES = {
  container: "...",      // Max-width 600px centered layout
  header: "...",         // Maroon gradient header
  headerTitle: "...",    // White text for header
  body: "...",           // White background body
  bodyText: "...",       // Gray text paragraphs
  button: "...",         // Maroon primary button
  buttonSecondary: "...", // White bordered button
  infoBox: "...",        // Gray background info card
  proTipBox: "...",      // Gold accent tip box
  footer: "...",         // Gray footer
  // ... more
};

Existing Templates (12)

Template IDPurposeKey Data Fields
welcomeNew student onboardingname
role_changeUser role changeduserName, oldRole, newRole
assistant_publishedTeacher's assistant goes liveassistantName, courseName, joinCode
budget_warningAdmin usage alertpercentUsed, currentSpend, budgetLimit
processing_failedFile upload errorfileName, errorMessage
new_student_joinedAdmin notificationstudentName, studentEmail, joinedAt
role_requestAdmin action neededuserName, userEmail, requestedRole
role_approvedRole request approvedname, newRole, tips[], dashboardUrl
role_deniedRole request deniedname, requestedRole, reason
teacher_digestWeekly analyticstotalSessions, uniqueStudents, satisfactionRate, topTopic
qr_join_codeQR code invitationassistantName, courseName, joinCode, qrCodeUrl

Adding a New Template

Step 1: Add to EMAIL_TEMPLATES object

// In lib/​jobs/​handlers/​email-send.ts

const EMAIL_TEMPLATES = {
  // ... existing templates

  // Your new template
  my_new_template: (data) => ({
    subject: `Your subject with ${escapeHtml(data.dynamicValue)}`,
    html: wrapInBaseTemplate({
      headerTitle: "Header Title",
      headerSubtitle: "Optional subtitle",
      content: `
        <p style="${STYLES.bodyText}">
          ${escapeHtml(data.message)}
        </​p>
        ${createButton("Action", data.actionUrl)}
      `,
      proTip: "Optional helpful tip",
    }),
  }),
};

Step 2: Add payload schema (if needed)

If your template needs specific data validation, update lib/​jobs/​types.ts:

export const EmailSendPayloadSchema = z.object({
  tenantId: z.string().uuid(),
  templateId: z.string(), // Template ID
  to: z.string().email(),
  subject: z.string().optional(), // Override template subject
  data: z.record(z.unknown()), // Template-specific data
});

Step 3: Trigger the email

import { publishJob } from "@/​lib/​jobs/​publisher";

await publishJob({
  jobType: "email:send",
  payload: {
    tenantId: user.tenantId,
    templateId: "my_new_template",
    to: user.email,
    data: {
      message: "Hello world!",
      actionUrl: "https://elon-ai.app/​action",
    },
  },
});

Step 4: Generate preview

Add test data and regenerate previews:

pnpm email:preview

Security Requirements

XSS Protection

ALWAYS use escapeHtml() on user-controlled content:

// CORRECT - escaped
<p>${escapeHtml(data.userName)}</​p>

// WRONG - XSS vulnerability!
<p>${data.userName}</​p>

FERPA Compliance

  • Include tenantId in all email payloads
  • All sends are logged to audit_logs table
  • Don't include student PII in email subjects
  • Unsubscribe tokens use HMAC-SHA256 signing

Multi-Tenant Scoping

  • Emails are always scoped to a tenant
  • Job queue handles tenant isolation
  • Audit logs record tenant context

Email Client Compatibility

DO

  • Use PNG/​JPG images with absolute URLs
  • Use inline CSS styles (no classes)
  • Use table-based layouts for columns
  • Keep emails under 102KB
  • Test in Gmail, Outlook, Apple Mail

DON'T

  • Use SVG images (Gmail strips them)
  • Use CSS Grid or Flexbox
  • Use media queries (limited support)
  • Use external CSS files
  • Use JavaScript

Logo

The logo is a hosted PNG at public/​email-assets/​elon-ai-logo.png:

// Defined in lib/​jobs/​handlers/​email-send.ts
const EMAIL_LOGO_URL = "https://elon-ai.app/​email-assets/​elon-ai-logo.png";
const LOGO_IMG = `<img src="${EMAIL_LOGO_URL}" alt="Elon AI" width="48" height="48" />`;

To regenerate the logo PNG:

pnpm tsx scripts/​generate-email-logo.ts

Testing

Visual Previews

Open .email-previews/​index.html to browse all templates visually.

Unit Tests

pnpm test tests/​unit/​email/

Integration Tests

pnpm test tests/​integration/​api/​unsubscribe.test.ts

Development Mode

When RESEND_API_KEY is not set, emails are logged to console instead of sent.

Brand Colors

ColorHexUsage
Maroon#73000aHeaders, buttons, primary text
Gold#b59a57Accents, pro tips, highlights
White#ffffffBody backgrounds, button text
Gray-50#f9fafbFooter, info box backgrounds
Gray-700#374151Body text

Common Patterns

Teacher Analytics Email

The teacher_digest template demonstrates advanced patterns:

  • Dynamic headlines based on metrics
  • Conditional content based on activity level
  • Unsubscribe link with HMAC token
  • Color-coded metric changes

QR Code Email

The qr_join_code template shows how to include images:

  • Hosted QR code image URL
  • Fallback text if image fails
  • Clear call-to-action

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View