Email SDK Reference
Complete reference for @wraps.dev/email TypeScript SDK.
A TypeScript-first SDK for sending emails through your Wraps-deployed AWS SES infrastructure. Simple, type-safe, and intuitive API with React.email support.
npm install @wraps.dev/email
import { WrapsEmail } from '@wraps.dev/email';const email = new WrapsEmail();const result = await email.send({ from: 'hello@yourdomain.com', to: 'user@example.com', subject: 'Welcome!', html: '<h1>Hello!</h1>',});console.log('Message ID:', result.messageId);Create a new WrapsEmail client. The SDK automatically detects AWS credentials from your environment.
new WrapsEmail(config?: WrapsEmailConfig)region (optional): AWS region where your infrastructure is deployed. Defaults to us-east-1credentials (optional): AWS credentials object. Auto-detected if not provided.endpoint (optional): Custom SES endpoint (for testing with LocalStack).AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)~/.aws/credentials)Example with Options
const email = new WrapsEmail({ region: 'us-west-2', credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, sessionToken: process.env.AWS_SESSION_TOKEN, // optional },});Testing with LocalStack
// Testing with LocalStackconst email = new WrapsEmail({ region: 'us-east-1', endpoint: 'http://localhost:4566',});WrapsEmailConfig Type
type WrapsEmailConfig = { region?: string; // AWS region (default: 'us-east-1') credentials?: { // Explicit AWS credentials accessKeyId: string; secretAccessKey: string; sessionToken?: string; }; roleArn?: string; // IAM role for OIDC assumption endpoint?: string; // Custom endpoint (e.g., LocalStack) configurationSetName?: string; // SES configuration set name tableName?: string; // DynamoDB table name override accountId?: string; // AWS account ID (for events) suppressionListName?: string; // Custom suppression list name maxRetries?: number; // Max AWS SDK retries (default: 3) timeout?: number; // Request timeout in ms (default: 30000) logger?: Logger; // Custom logger instance};Send an email using AWS SES through your Wraps infrastructure.
email.send(params: SendEmailParams): Promise<SendEmailResult>| Parameter | Type | Description |
|---|---|---|
from | string | Sender email address (must be verified) |
to | string | string[] | Recipient email address(es) |
subject | string | Email subject line |
html | string | HTML email body (optional) |
text | string | Plain text email body (optional) |
react | ReactElement | React.email component (optional) |
attachments | Attachment[] | File attachments (optional) |
tags | Record<string, string> | SES message tags (optional) |
cc, bcc, replyTo | string | string[] | Additional recipients (optional) |
Basic Email
const result = await email.send({ from: 'hello@yourdomain.com', to: 'user@example.com', subject: 'Welcome to our app', html: '<h1>Welcome!</h1><p>Thanks for signing up.</p>', text: 'Welcome! Thanks for signing up.',});console.log('Message ID:', result.messageId);console.log('Request ID:', result.requestId);Multiple Recipients
const result = await email.send({ from: 'newsletter@yourdomain.com', to: ['user1@example.com', 'user2@example.com'], cc: 'manager@yourdomain.com', bcc: ['archive@yourdomain.com'], replyTo: 'support@yourdomain.com', subject: 'Weekly Newsletter', html: '<h1>This week\'s updates</h1>',});With Tags
// Add SES tags for tracking and analyticsawait email.send({ from: 'you@yourdomain.com', to: 'user@example.com', subject: 'Newsletter', html: '<p>Content</p>', tags: { campaign: 'newsletter-2025-01', type: 'marketing', }, configurationSetName: 'wraps-email-tracking', // optional});Use React.email components for beautiful, type-safe email templates. The SDK automatically renders React components to HTML and plain text.
import { WelcomeEmail } from './emails/Welcome';// Send using React.email componentawait email.send({ from: 'hello@yourdomain.com', to: 'user@example.com', subject: 'Welcome to our platform', react: <WelcomeEmail name="John" orderId="12345" />,});Send emails with file attachments (PDFs, images, documents, etc.). The SDK automatically handles MIME encoding.
// Single attachmentconst result = await email.send({ from: 'you@yourdomain.com', to: 'user@example.com', subject: 'Your invoice', html: '<p>Please find your invoice attached.</p>', attachments: [ { filename: 'invoice.pdf', content: pdfBuffer, // Buffer or base64 string contentType: 'application/pdf', // Optional - auto-detected }, ],});// Multiple attachmentsawait email.send({ from: 'you@yourdomain.com', to: 'user@example.com', subject: 'Monthly Report', html: '<h1>Monthly Report</h1>', attachments: [ { filename: 'report.pdf', content: pdfBuffer }, { filename: 'chart.png', content: imageBuffer }, { filename: 'data.csv', content: csvBuffer }, ],});| Field | Type | Description |
|---|---|---|
filename | string | Filename with extension |
content | Buffer | string | File content (Buffer or base64 string) |
contentType | string | MIME type (optional - auto-detected from filename) |
SES templates allow you to store reusable email designs with variables in your AWS account.
Create Template
// Create a template with variablesawait email.templates.create({ name: 'welcome-email', subject: 'Welcome to {{companyName}}, {{name}}!', html: ` <h1>Welcome {{name}}!</h1> <p>Click to confirm: <a href="{{confirmUrl}}">Confirm Account</a></p> `, text: 'Welcome {{name}}! Click to confirm: {{confirmUrl}}',});Create from React.email
import { WelcomeEmailTemplate } from './emails/Welcome';// Create template from React.email componentawait email.templates.createFromReact({ name: 'welcome-email-v2', subject: 'Welcome to {{companyName}}, {{name}}!', react: <WelcomeEmailTemplate />, // React component should use {{variable}} syntax});Manage Templates
// Get template detailsconst template = await email.templates.get('welcome-email');console.log(template.name, template.subject);// List all templatesconst templates = await email.templates.list();templates.forEach(t => console.log(t.name, t.createdTimestamp));// Update a templateawait email.templates.update({ name: 'welcome-email', subject: 'Welcome aboard, {{name}}!', html: '<h1>Welcome {{name}}!</h1>...',});// Delete a templateawait email.templates.delete('welcome-email');| Method | Description |
|---|---|
templates.create(params) | Create a new SES template |
templates.createFromReact(params) | Create template from React component |
templates.update(params) | Update an existing template |
templates.get(name) | Get template details |
templates.list() | List all templates |
templates.delete(name) | Delete a template |
Send an email using a pre-defined SES template. Templates support variable substitution using {{variable}} syntax.
email.sendTemplate(params: SendTemplateParams): Promise<SendEmailResult>const result = await email.sendTemplate({ from: 'hello@yourdomain.com', to: 'user@example.com', template: 'welcome-email', templateData: { name: 'John Doe', companyName: 'Acme Corp', confirmUrl: 'https://example.com/confirm/abc123', },});console.log('Email sent:', result.messageId);Send personalized templated emails to multiple recipients (up to 50 per call). Each recipient can have unique template data merged with default values.
email.sendBulkTemplate(params: SendBulkTemplateParams): Promise<SendBulkTemplateResult>const result = await email.sendBulkTemplate({ from: 'hello@yourdomain.com', template: 'weekly-digest', destinations: [ { to: 'alice@example.com', templateData: { name: 'Alice', unreadCount: 5 }, }, { to: 'bob@example.com', templateData: { name: 'Bob', unreadCount: 12 }, }, ], defaultTemplateData: { companyName: 'Acme Corp', year: '2025', },});// Check results for each recipientresult.status.forEach((item, i) => { if (item.status === 'success') { console.log(`Email ${i + 1} sent: ${item.messageId}`); } else { console.log(`Email ${i + 1} failed: ${item.error}`); }});Read, reply to, and forward inbound emails. Requires inbound email infrastructure to be deployed with wraps email inbound init.
Initialize with Inbox
const email = new WrapsEmail({ inboundBucket: 'your-inbound-bucket-name',});List Inbound Emails
const { emails, nextToken } = await email.inbox.list({ maxResults: 20, continuationToken: undefined, // for pagination});for (const summary of emails) { console.log(summary.emailId, summary.key, summary.lastModified);}Get Email Details
const inboundEmail = await email.inbox.get('email-abc123');console.log('From:', inboundEmail.from.address);console.log('Subject:', inboundEmail.subject);console.log('HTML:', inboundEmail.html);console.log('Attachments:', inboundEmail.attachments.length);console.log('Spam:', inboundEmail.spamVerdict);console.log('Virus:', inboundEmail.virusVerdict);Reply to Email
// Reply with proper threading headers (In-Reply-To, References)const result = await email.inbox.reply('email-abc123', { from: 'support@yourdomain.com', text: 'Thanks for reaching out!', html: '<p>Thanks for reaching out!</p>', attachments: [], // optional});console.log('Reply sent:', result.messageId);Forward Email
// Passthrough mode (default): rewrites headers, keeps original bodyconst result = await email.inbox.forward('email-abc123', { from: 'noreply@yourdomain.com', to: 'team@yourdomain.com', addPrefix: '[Customer]', // optional subject prefix});// Wrapped mode: builds new message with forwarded contentconst result2 = await email.inbox.forward('email-abc123', { from: 'noreply@yourdomain.com', to: 'team@yourdomain.com', passthrough: false, text: 'FYI - see below', // optional intro text});Get Attachment URL
// Get presigned URL for an attachment (valid for 1 hour by default)const url = await email.inbox.getAttachment('email-abc123', 'attachment-id', { expiresIn: 3600, // seconds});Delete Email
// Delete email and all associated files (parsed JSON, raw MIME, attachments)await email.inbox.delete('email-abc123');| Method | Description |
|---|---|
inbox.list(options?) | List inbound emails with pagination |
inbox.get(emailId) | Get full email details by ID |
inbox.reply(emailId, options) | Reply with threading headers |
inbox.forward(emailId, options) | Forward to new recipients |
inbox.getAttachment(emailId, attachmentId, options?) | Get presigned URL for attachment |
inbox.getRaw(emailId) | Get presigned URL for raw MIME email |
inbox.delete(emailId) | Delete email and all associated files |
Track the delivery lifecycle of every email. Requires event tracking infrastructure deployed with wraps email init (Production or Enterprise preset).
Initialize with Events
const email = new WrapsEmail({ historyTableName: 'wraps-email-history',});Get Email Status
// Get full status and event timeline for an emailconst status = await email.events.get('message-id-from-send');if (status) { console.log('Status:', status.status); // 'delivered', 'opened', 'bounced', etc. console.log('From:', status.from); console.log('To:', status.to); console.log('Subject:', status.subject); console.log('Sent at:', new Date(status.sentAt)); console.log('Last event:', new Date(status.lastEventAt)); // Full event timeline for (const event of status.events) { console.log(` ${event.type} at ${new Date(event.timestamp)}`); if (event.metadata) console.log(' metadata:', event.metadata); }}List Emails with Status
// List recent emails with status (paginated)const { emails, nextToken } = await email.events.list({ accountId: '123456789012', startTime: new Date('2025-01-01'), maxResults: 20,});for (const item of emails) { console.log(`${item.messageId}: ${item.status} - ${item.subject}`);}// Fetch next pageif (nextToken) { const nextPage = await email.events.list({ accountId: '123456789012', continuationToken: nextToken, });}| Status | Description |
|---|---|
sent | Email accepted by SES |
delivered | Email delivered to recipient's mail server |
opened | Recipient opened the email (tracking pixel) |
clicked | Recipient clicked a link in the email |
bounced | Email bounced (hard or soft bounce) |
complained | Recipient marked as spam |
suppressed | Email suppressed by SES (on suppression list) |
Negative statuses (bounced, complained, suppressed) always override positive ones. Status is derived from the highest-priority event.
| Method | Description |
|---|---|
events.get(messageId) | Get full status and event timeline for an email |
events.list(options) | List emails with status, filtered by account and time range |
Manage the SES account-level suppression list. Emails on this list are automatically blocked from sending. The suppression API is always available — no extra configuration needed.
Check if Suppressed
// Check if an email is suppressedconst entry = await email.suppression.get('user@example.com');if (entry) { console.log('Suppressed:', entry.email); console.log('Reason:', entry.reason); // 'BOUNCE' or 'COMPLAINT' console.log('Since:', entry.lastUpdated); console.log('Message ID:', entry.messageId); // original bounce/complaint message} else { console.log('Email is not suppressed');}Add to Suppression List
// Manually add an email to the suppression listawait email.suppression.add('bad-address@example.com', 'BOUNCE');// Also works for complaintsawait email.suppression.add('unsubscribed@example.com', 'COMPLAINT');Remove from Suppression List
// Remove from suppression list (idempotent — won't throw if not found)await email.suppression.remove('user@example.com');List Suppressed Emails
// List all suppressed emails (paginated)const { entries, nextToken } = await email.suppression.list({ reason: 'BOUNCE', // optional: filter by reason startDate: new Date('2025-01-01'), // optional maxResults: 100, // default: 100, max: 1000});for (const entry of entries) { console.log(`${entry.email}: ${entry.reason} (since ${entry.lastUpdated})`);}// Paginateif (nextToken) { const nextPage = await email.suppression.list({ continuationToken: nextToken, });}| Method | Description |
|---|---|
suppression.get(email) | Check if an email is suppressed (returns null if not) |
suppression.add(email, reason) | Add email to suppression list |
suppression.remove(email) | Remove from suppression list (idempotent) |
suppression.list(options?) | List suppressed emails with filters and pagination |
The SDK throws typed errors for different failure scenarios.
| Error | Description |
|---|---|
ValidationError | Invalid input parameters (includes field property) |
SESError | AWS SES error (includes code, requestId, retryable properties) |
DynamoDBError | DynamoDB error from events API (includes code, requestId, retryable properties) |
import { WrapsEmail, SESError, DynamoDBError, ValidationError } from '@wraps.dev/email';try { await email.send({ ... });} catch (error) { if (error instanceof ValidationError) { // Invalid email address, missing required fields, etc. console.error('Validation error:', error.message); console.error('Field:', error.field); } else if (error instanceof SESError) { // AWS SES error (rate limit, unverified sender, etc.) console.error('SES error:', error.message); console.error('Code:', error.code); // 'MessageRejected', 'Throttling' console.error('Request ID:', error.requestId); console.error('Retryable:', error.retryable); } else if (error instanceof DynamoDBError) { // DynamoDB error (events API) console.error('DynamoDB error:', error.message); console.error('Code:', error.code); console.error('Retryable:', error.retryable); }}The SDK is written in TypeScript and provides full type safety out of the box.
import { WrapsEmail, SendEmailParams, SendEmailResult, SendTemplateParams,} from '@wraps.dev/email';const email = new WrapsEmail();// TypeScript validates all parametersconst params: SendEmailParams = { from: 'hello@yourdomain.com', to: 'user@example.com', subject: 'Test', html: '<p>Test</p>',};const result: SendEmailResult = await email.send(params);Important defaults and limits to keep in mind:
retryable is trueCheck out the package on npm for the latest version and changelog.
View PackageExplore the source code, report issues, or contribute.
View SourceLearn how to build beautiful email templates with React.
Learn More