Transactional Email API Guide: Verification, Password Reset, and Notifications

Transactional emails are personalized emails triggered by user actions, such as registration verification, password reset, and order confirmations. Unlike marketing emails, transactional emails are "expected" by users, resulting in 70-90% open rates.

1. Transactional vs Marketing Email

Feature Transactional Marketing
Trigger User action Scheduled batch
Personalization High Medium
Open rate 70-90% 20-30%
Frequency On demand Scheduled
Unsubscribe Should not include Must include
Delivery requirement Must deliver Best effort

A high open rate is a double-edged sword: precisely because users expect these emails, a failure to deliver, a trip to the spam folder, or a broken link costs far more than a single message — it costs trust in your product. A user who never receives a password reset may churn to a competitor; a late order confirmation means an extra support ticket. Treat transactional email as a critical path, not a marketing channel.

2. Common Email Types

2.1 Registration Verification

const crypto = require('crypto');
function generateVerificationToken(userId) {
  const token = crypto.randomBytes(32).toString('hex');
  const expiresAt = Date.now() + 24 * 60 * 60 * 1000;
  saveToken({ userId, token, expiresAt, type: 'email_verification' });
  return token;
}

2.2 Password Reset

async function requestPasswordReset(email) {
  const user = await findUserByEmail(email);
  if (!user) return { success: true }; // Don't reveal user existence
  const token = crypto.randomBytes(32).toString('hex');
  const resetUrl = `https://yourdomain.com/reset-password?token=${token}`;
  await emailService.send({
    to: email,
    template: 'password-reset',
    data: { username: user.name, resetUrl },
  });
}

2.3 Order Notifications

async function sendOrderConfirmation(order) {
  await emailService.send({
    to: order.customerEmail,
    template: 'order-confirmation',
    data: {
      orderNumber: order.number,
      customerName: order.customerName,
      items: order.items.map(item => ({
        name: item.name,
        quantity: item.quantity,
        price: formatPrice(item.price),
      })),
      total: formatPrice(order.total),
      estimatedDelivery: order.estimatedDelivery,
      trackingUrl: order.trackingUrl,
    },
  });
}

2.4 Comparing Main Providers

Provider Free tier Highlights Best for
SendGrid 100/month Mature ecosystem, rich templates and analytics Mid-to-large scale, detailed reporting
Mailgun 100/month Flexible API, developer-friendly Deeply customized sending logic
Resend 3,000/month Native React Email support, quick to start Small teams, fast validation
AWS SES Pay per use (~$0.10/thousand) Lowest price, AWS-native High volume, cost-sensitive

Beyond price, check three things: whether delivery and bounce events are reported via webhooks, whether a dedicated IP is available, and the limits of the free tier (many free quotas cap daily sends). Abstracting the provider behind a single EmailService interface (below) lets you switch providers without rewriting business code.

3. Template Management

  • One template per scenario
  • Parameterized with placeholders
  • Multi-language support
  • Responsive design
  • Plain text version alongside HTML

Creating a separate template per scenario is not extra code for its own sake; it lets each email's structure and copy evolve independently. Order confirmations need a product list and tracking links, password resets need clear security guidance, and verification emails must state the expiry — forcing all of these into one template only gets messier. Version your templates after launch too: when a copy change drags down open rates, you can roll back and compare quickly. Don't skip the plain-text version — some clients and assistive tools render text only, and omitting it means giving up on part of your audience.

4. System Design

4.1 Queue Architecture

App Service → Message Queue (Redis/RabbitMQ) → Email Worker → Email API → Logging & Monitoring

Benefits: Async processing, retry mechanism, rate limiting, horizontal scaling

A realistic scenario: a sale day that sends 100,000 order confirmations. If every email is sent synchronously inside the order request, the checkout endpoint slows to hundreds of milliseconds and both the database and the mail provider get hammered. The right approach is to push send tasks into a queue (Redis or RabbitMQ) and let standalone workers consume them asynchronously: the API responds instantly, workers respect the provider's rate limits, and failures re-enter a retry queue. This addresses three problems at once — user-facing response time, provider rate limiting, and retries. Even if a batch of emails is delayed by half an hour, users generally find that acceptable.

5. Security

5.1 Rate Limiting

function checkRateLimit(email, type) {
  const key = `${email}:${type}`;
  const windowMs = 60 * 1000;
  const maxAttempts = 3;
  // Check and enforce limits
}

5.2 Token Security

  • Use crypto.randomBytes for token generation
  • Set reasonable expiration
  • Invalidate tokens after use
  • Track failed attempts

5.3 Deliverability: SPF / DKIM / DMARC

However important a transactional email is, it is worthless if it never reaches the inbox. Deliverability rests on three records: SPF declares which servers are allowed to send mail for your domain, DKIM signs the message digitally, and DMARC tells recipients what to do with forged mail. Most providers hand you the exact DNS records during onboarding — just paste them into your domain registrar. Before launch, verify the configuration with Google Postmaster Tools or Mail Tester; details are in the email deliverability guide.

6. Monitoring

Metric Alert Threshold Description
Send failure rate > 3% Rejected or timed out
Latency > 5 minutes Send to delivery delay
Bounce rate > 2% Invalid addresses
Queue backlog > 1000 Queue accumulation

Alongside hard metrics like send failure rate and latency, watch the health of the content itself: a sudden drop in open rate may mean a broken template or a trip to spam; abnormal click rates can indicate links blocked by email clients; a persistently rising bounce rate points to weak address validation at signup. Feed webhook events (delivered, opened, clicked, bounced, complained) into your logs so every email has a complete lifecycle record.

6.3 Pre-Launch Checklist

  • A dedicated, parameterized template for each sending scenario
  • Tokens generated with crypto.randomBytes, expiring within 24 hours, invalidated after use
  • Password reset does not reveal whether an account exists
  • Sending runs through a queue + workers with automatic retries
  • SPF/DKIM/DMARC configured and verified
  • Webhook events persisted and key metrics alerted

7. Summary

Transactional emails are critical for user communication. Use mature third-party services, implement queues and retry mechanisms, and don't forget monitoring.

Reference: RFC 5321 (SMTP protocol) https://datatracker.ietf.org/doc/html/rfc5321
Reference: Google Postmaster Tools https://postmaster.google.com
Reference: React Email documentation https://react.email/docs/introduction