
wraps-email
TypeScript SDK for AWS SES with automatic credential resolution, React.email support, and template management.
TypeScript SDK for AWS SES with automatic credential resolution, React.email support, and template management.
@wraps.dev/email SDK
TypeScript SDK for AWS SES with automatic credential resolution, React.email support, and template management. Calls your SES directly — no proxy, no markup.
Installation
npm install @wraps.dev/email
# or
pnpm add @wraps.dev/email
Quick Start
import { WrapsEmail } from '@wraps.dev/email';
const email = new WrapsEmail();
const result = await email.send({
from: 'hello@yourapp.com',
to: 'user@example.com',
subject: 'Welcome!',
html: '<h1>Hello from Wraps!</h1>',
});
console.log('Sent:', result.messageId);
Client Configuration
Default (Auto-detect credentials)
// Uses AWS credential chain (env vars, IAM role, ~/.aws/credentials)
const email = new WrapsEmail();
With Region
const email = new WrapsEmail({
region: 'us-west-2', // defaults to us-east-1
});
With Explicit Credentials
const email = new WrapsEmail({
region: 'us-east-1',
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
});
With IAM Role (OIDC / Cross-account)
// For Vercel, EKS, GitHub Actions with OIDC federation
const email = new WrapsEmail({
region: 'us-east-1',
roleArn: 'arn:aws:iam::123456789012:role/WrapsEmailRole',
roleSessionName: 'my-app-session', // optional
});
With Credential Provider (Advanced)
import { fromWebToken } from '@aws-sdk/credential-providers';
const credentials = fromWebToken({
roleArn: process.env.AWS_ROLE_ARN!,
webIdentityToken: async () => process.env.VERCEL_OIDC_TOKEN!,
});
const email = new WrapsEmail({
region: 'us-east-1',
credentials,
});
With Pre-configured SES Client
import { SESClient } from '@aws-sdk/client-ses';
const sesClient = new SESClient({ region: 'us-east-1' });
const email = new WrapsEmail({ client: sesClient });
Sending Emails
Simple Email
const result = await email.send({
from: 'sender@example.com',
to: 'recipient@example.com',
subject: 'Hello!',
html: '<h1>Welcome</h1><p>This is a test email.</p>',
text: 'Welcome! This is a test email.', // optional fallback
});
With Named Sender
await email.send({
from: { email: 'hello@example.com', name: 'My App' },
to: 'user@example.com',
subject: 'Welcome!',
html: '<h1>Hello!</h1>',
});
Multiple Recipients
await email.send({
from: 'sender@example.com',
to: ['user1@example.com', 'user2@example.com'],
cc: 'manager@example.com',
bcc: ['audit@example.com'],
subject: 'Team Update',
html: '<p>Hello team!</p>',
});
With Reply-To
await email.send({
from: 'noreply@example.com',
to: 'user@example.com',
replyTo: 'support@example.com',
subject: 'Your Request',
html: '<p>We received your request.</p>',
});
With Tags (for SES tracking)
await email.send({
from: 'sender@example.com',
to: 'user@example.com',
subject: 'Welcome!',
html: '<h1>Hello!</h1>',
tags: {
campaign: 'onboarding',
userId: 'user_123',
},
});
With Configuration Set
await email.send({
from: 'sender@example.com',
to: 'user@example.com',
subject: 'Welcome!',
html: '<h1>Hello!</h1>',
configurationSetName: 'wraps-email-tracking', // for opens/clicks/bounces
});
React.email Integration
Use React components for beautiful, maintainable email templates.
import { WrapsEmail } from '@wraps.dev/email';
import WelcomeEmail from './emails/welcome';
const email = new WrapsEmail();
await email.send({
from: 'hello@example.com',
to: 'user@example.com',
subject: 'Welcome to Our App!',
react: <WelcomeEmail username="John" />,
});
Note: Cannot use both html and react — choose one.
Attachments
import { readFileSync } from 'fs';
await email.send({
from: 'sender@example.com',
to: 'user@example.com',
subject: 'Your Invoice',
html: '<p>Please find your invoice attached.</p>',
attachments: [
{
filename: 'invoice.pdf',
content: readFileSync('./invoice.pdf'),
contentType: 'application/pdf',
},
{
filename: 'logo.png',
content: Buffer.from(base64Logo, 'base64'),
contentType: 'image/png',
},
],
});
Limits:
- Maximum 100 attachments per email
- Total message size: 10MB (AWS SES limit)
Templates
SES templates allow personalized bulk emails with variable substitution.
Create Template
await email.templates.create({
name: 'welcome-email',
subject: 'Welcome, {{name}}!',
html: '<h1>Hello {{name}}</h1><p>Thanks for joining {{company}}!</p>',
text: 'Hello {{name}}, Thanks for joining {{company}}!',
});
Create Template from React
import WelcomeTemplate from './emails/welcome-template';
await email.templates.createFromReact({
name: 'welcome-email',
subject: 'Welcome, {{name}}!',
react: <WelcomeTemplate />, // Use {{variable}} placeholders in the component
});
Send with Template
await email.sendTemplate({
from: 'hello@example.com',
to: 'user@example.com',
template: 'welcome-email',
templateData: {
name: 'John',
company: 'Acme Inc',
},
});
Bulk Send with Template
const result = await email.sendBulkTemplate({
from: 'hello@example.com',
template: 'welcome-email',
destinations: [
{ to: 'user1@example.com', templateData: { name: 'Alice', company: 'Acme' } },
{ to: 'user2@example.com', templateData: { name: 'Bob', company: 'Acme' } },
{ to: 'user3@example.com', templateData: { name: 'Carol', company: 'Acme' } },
],
defaultTemplateData: {
company: 'Acme Inc', // fallback if not in destination
},
});
// Check results
result.status.forEach((s, i) => {
if (s.status === 'success') {
console.log(`Email ${i} sent: ${s.messageId}`);
} else {
console.log(`Email ${i} failed: ${s.error}`);
}
});
Limit: Maximum 50 destinations per bulk send.
Manage Templates
// List all templates
const templates = await email.templates.list();
// Get template details
const template = await email.templates.get('welcome-email');
// Update template
await email.templates.update({
name: 'welcome-email',
subject: 'Welcome aboard, {{name}}!',
html: '<h1>Welcome {{name}}!</h1>',
});
// Delete template
await email.templates.delete('welcome-email');
Error Handling
import { WrapsEmail, SESError, ValidationError } from '@wraps.dev/email';
try {
await email.send({
from: 'sender@example.com',
to: 'user@example.com',
subject: 'Hello',
html: '<p>Hi!</p>',
});
} catch (error) {
if (error instanceof ValidationError) {
// Invalid parameters (e.g., invalid email format)
console.error('Validation error:', error.message);
} else if (error instanceof SESError) {
// AWS SES error
console.error('SES error:', error.message);
console.error('Error code:', error.code);
console.error('Request ID:', error.requestId);
console.error('Is throttled:', error.isThrottled);
} else {
throw error;
}
}
Cleanup
// When done (e.g., in serverless cleanup or app shutdown)
email.destroy();
Type Exports
import type {
WrapsEmailConfig,
SendEmailParams,
SendEmailResult,
SendTemplateParams,
SendBulkTemplateParams,
SendBulkTemplateResult,
CreateTemplateParams,
UpdateTemplateParams,
Template,
TemplateMetadata,
EmailAddress,
Attachment,
} from '@wraps.dev/email';
Common Patterns
Transactional Email Service
import { WrapsEmail } from '@wraps.dev/email';
class EmailService {
private email: WrapsEmail;
constructor() {
this.email = new WrapsEmail({
region: process.env.AWS_REGION,
configurationSetName: 'wraps-email-tracking',
});
}
async sendWelcome(to: string, name: string) {
return this.email.send({
from: { email: 'hello@myapp.com', name: 'My App' },
to,
subject: `Welcome, ${name}!`,
html: `<h1>Welcome ${name}!</h1><p>Thanks for signing up.</p>`,
tags: { type: 'welcome', userId: to },
});
}
async sendPasswordReset(to: string, resetLink: string) {
return this.email.send({
from: 'security@myapp.com',
to,
subject: 'Reset Your Password',
html: `<p>Click <a href="${resetLink}">here</a> to reset your password.</p>`,
tags: { type: 'password-reset' },
});
}
}
Vercel Edge/Serverless
import { WrapsEmail } from '@wraps.dev/email';
// Initialize outside handler for connection reuse
const email = new WrapsEmail({
roleArn: process.env.AWS_ROLE_ARN,
});
export async function POST(request: Request) {
const { to, subject, html } = await request.json();
const result = await email.send({
from: 'noreply@myapp.com',
to,
subject,
html,
});
return Response.json({ messageId: result.messageId });
}
Requirements
- Node.js 18+
- AWS SES configured (use
npx @wraps.dev/cli email initfor easy setup) - Verified domain or email address in SES
You Might Also Like
Related Skills

gog
Google Workspace CLI for Gmail, Calendar, Drive, Contacts, Sheets, and Docs.
openclaw
orpc-contract-first
Guide for implementing oRPC contract-first API patterns in Dify frontend. Triggers when creating new API contracts, adding service endpoints, integrating TanStack Query with typed contracts, or migrating legacy service calls to oRPC. Use for all API layer work in web/contract and web/service directories.
langgenius
