Services SDK
Email Service

Email Service

Transactional email with Resend integration and template support.

Installation

pnpm add @aicr/email-service

Configuration

Set the RESEND_API_KEY environment variable:

RESEND_API_KEY=re_xxxxxxxxxxxx
EMAIL_FROM=noreply@yourdomain.com  # Optional default sender

Quick Start

import { sendEmail, sendTemplateEmail, createEmailClient } from '@aicr/email-service';
 
// Send simple email
await sendEmail({
  to: 'user@example.com',
  subject: 'Welcome!',
  html: '<h1>Welcome to our platform</h1>'
});
 
// Send template email
await sendTemplateEmail('welcome', {
  to: 'user@example.com',
  data: { name: 'John', activationUrl: 'https://...' }
});

API Reference

createEmailClient(config)

Create a configured email client.

import { createEmailClient } from '@aicr/email-service';
 
const email = createEmailClient({
  apiKey: process.env.RESEND_API_KEY,
  defaultFrom: 'noreply@company.com',
  defaultReplyTo: 'support@company.com',
  debug: process.env.NODE_ENV === 'development'
});
 
await email.send({ to: 'user@example.com', subject: 'Hello', html: '...' });

sendEmail(options)

Send an email using the default client.

interface SendEmailOptions {
  to: string | string[];
  subject: string;
  html?: string;
  text?: string;
  from?: string;
  replyTo?: string;
  cc?: string | string[];
  bcc?: string | string[];
  attachments?: Attachment[];
  headers?: Record<string, string>;
  tags?: { name: string; value: string }[];
}
 
interface Attachment {
  filename: string;
  content: string | Buffer;
  contentType?: string;
}

Returns: Promise<EmailResult>

interface EmailResult {
  success: boolean;
  id?: string;        // Resend message ID
  error?: string;
  errorCode?: string;
}

sendTemplateEmail(templateName, options)

Send an email using a registered template.

await sendTemplateEmail('analysis-complete', {
  to: 'user@example.com',
  data: {
    documentName: 'Q4 Compensation Plan',
    resultUrl: 'https://app.example.com/results/123',
    completedAt: new Date().toISOString()
  }
});

Options:

interface SendTemplateOptions {
  to: string | string[];
  data: Record<string, unknown>;
  subject?: string;        // Override template subject
  from?: string;
  cc?: string | string[];
  attachments?: Attachment[];
}

registerTemplate(template)

Register a custom email template.

import { getEmailClient } from '@aicr/email-service';
 
getEmailClient().registerTemplate({
  name: 'invoice',
  subject: 'Invoice #{{invoiceNumber}}',
  html: `
    <h1>Invoice #{{invoiceNumber}}</h1>
    <p>Dear {{customerName}},</p>
    <p>Your invoice for {{amount}} is attached.</p>
    <p>Due date: {{dueDate}}</p>
  `,
  text: 'Invoice #{{invoiceNumber}} for {{amount}}'
});
 
// Use it
await sendTemplateEmail('invoice', {
  to: 'customer@example.com',
  data: {
    invoiceNumber: 'INV-001',
    customerName: 'Acme Corp',
    amount: '$1,500.00',
    dueDate: '2024-02-15'
  }
});

Built-in Templates

TemplateSubjectVariables
welcomeWelcome to {{appName}}name, appName, activationUrl
analysis-completeAnalysis Complete: {{documentName}}documentName, resultUrl, completedAt
approval-requestApproval Required: {{title}}title, requestedBy, approvalUrl, expiresAt
password-resetReset Your Passwordname, resetUrl, expiresIn

Template Syntax

Templates use {{variable}} syntax with support for nested objects:

<p>Hello {{user.name}},</p>
<p>Your order {{order.id}} for {{order.total}} has shipped.</p>
<p>Track it here: {{tracking.url}}</p>
await sendTemplateEmail('order-shipped', {
  to: 'customer@example.com',
  data: {
    user: { name: 'John' },
    order: { id: 'ORD-123', total: '$99.00' },
    tracking: { url: 'https://...' }
  }
});

Error Handling

const result = await sendEmail({
  to: 'user@example.com',
  subject: 'Test',
  html: '<p>Hello</p>'
});
 
if (!result.success) {
  switch (result.errorCode) {
    case 'NOT_CONFIGURED':
      console.error('Email service not configured');
      break;
    case 'INVALID_CONTENT':
      console.error('Email must have html or text content');
      break;
    case 'TEMPLATE_NOT_FOUND':
      console.error('Template not found');
      break;
    case 'SEND_FAILED':
      console.error('Failed to send:', result.error);
      break;
  }
}

Checking Configuration

import { getEmailClient } from '@aicr/email-service';
 
if (!getEmailClient().isConfigured()) {
  console.warn('Email service not configured - emails will not be sent');
}