elon-email-templates

Elon AI Email Templates

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "elon-email-templates" with this command: npx skills add omerakben/omer-akben/omerakben-omer-akben-elon-email-templates

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

File Purpose

lib/jobs/handlers/email-send.ts

All 12 email templates + handler (916 lines)

lib/email/constants.ts

Centralized colors, styles, logo URL

lib/email/unsubscribe-token.ts

HMAC-signed unsubscribe tokens

app/api/email/unsubscribe/route.ts

Unsubscribe API endpoint

lib/jobs/types.ts

EmailSendPayloadSchema 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

Function Purpose Example

wrapInBaseTemplate()

Header + content + footer wrapper See above

createButton()

Primary/secondary CTA buttons createButton("Click Me", url, "primary")

createInfoBox()

Key-value info cards createInfoBox([{label: "Name", value: "John"}])

escapeHtml()

XSS protection for user content escapeHtml(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 ID Purpose Key Data Fields

welcome

New student onboarding name

role_change

User role changed userName , oldRole , newRole

assistant_published

Teacher's assistant goes live assistantName , courseName , joinCode

budget_warning

Admin usage alert percentUsed , currentSpend , budgetLimit

processing_failed

File upload error fileName , errorMessage

new_student_joined

Admin notification studentName , studentEmail , joinedAt

role_request

Admin action needed userName , userEmail , requestedRole

role_approved

Role request approved name , newRole , tips[] , dashboardUrl

role_denied

Role request denied name , requestedRole , reason

teacher_digest

Weekly analytics totalSessions , uniqueStudents , satisfactionRate , topTopic

qr_join_code

QR code invitation assistantName , 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 = &#x3C;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

Color Hex Usage

Maroon #73000a

Headers, buttons, primary text

Gold #b59a57

Accents, pro tips, highlights

White #ffffff

Body backgrounds, button text

Gray-50 #f9fafb

Footer, info box backgrounds

Gray-700 #374151

Body 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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

bundle-optimization

No summary provided by upstream source.

Repository SourceNeeds Review
General

theme-factory

No summary provided by upstream source.

Repository SourceNeeds Review
General

slack-gif-creator

No summary provided by upstream source.

Repository SourceNeeds Review
General

api-route-creator

No summary provided by upstream source.

Repository SourceNeeds Review