Wraps Logo
DocsHome
SDK Reference

@wraps.dev/email 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.

Installation

npm install @wraps.dev/email

Quick Start

TypeScriptexample.ts
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);

Initialization

Create a new WrapsEmail client. The SDK automatically detects AWS credentials from your environment.

Constructor
new WrapsEmail(config?: WrapsEmailConfig)

Options

  • region (optional): AWS region where your infrastructure is deployed. Defaults to us-east-1
  • credentials (optional): AWS credentials object. Auto-detected if not provided.
  • endpoint (optional): Custom SES endpoint (for testing with LocalStack).

Authentication Order

  1. Explicit credentials passed to constructor
  2. Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
  3. Shared credentials file (~/.aws/credentials)
  4. IAM role (EC2, ECS, Lambda)

Example with Options

TypeScriptconfig.ts
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

TypeScriptlocalstack.ts
// Testing with LocalStackconst email = new WrapsEmail({  region: 'us-east-1',  endpoint: 'http://localhost:4566',});

WrapsEmailConfig Type

TypeScriptconfig-type.ts
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 Email

Send an email using AWS SES through your Wraps infrastructure.

Method
email.send(params: SendEmailParams): Promise<SendEmailResult>

Parameters

ParameterTypeDescription
fromstringSender email address (must be verified)
tostring | string[]Recipient email address(es)
subjectstringEmail subject line
htmlstringHTML email body (optional)
textstringPlain text email body (optional)
reactReactElementReact.email component (optional)
attachmentsAttachment[]File attachments (optional)
tagsRecord<string, string>SES message tags (optional)
cc, bcc, replyTostring | string[]Additional recipients (optional)

Examples

Basic Email

TypeScriptbasic-email.ts
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

TypeScriptmultiple-recipients.ts
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

TypeScriptwith-tags.ts
// 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});

React.email Support

Use React.email components for beautiful, type-safe email templates. The SDK automatically renders React components to HTML and plain text.

Reactreact-email.tsx
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" />,});

Attachments

Send emails with file attachments (PDFs, images, documents, etc.). The SDK automatically handles MIME encoding.

TypeScriptattachments.ts
// 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 },  ],});

Attachment Options

FieldTypeDescription
filenamestringFilename with extension
contentBuffer | stringFile content (Buffer or base64 string)
contentTypestringMIME type (optional - auto-detected from filename)

Limits

  • Maximum 100 attachments per email
  • Maximum message size: 10 MB (AWS SES limit)
  • Works with both HTML and React.email components

Template Management

SES templates allow you to store reusable email designs with variables in your AWS account.

Create Template

TypeScriptcreate-template.ts
// 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

Reactcreate-from-react.tsx
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

TypeScriptmanage-templates.ts
// 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');

Available Methods

MethodDescription
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 Template

Send an email using a pre-defined SES template. Templates support variable substitution using {{variable}} syntax.

Method
email.sendTemplate(params: SendTemplateParams): Promise<SendEmailResult>
TypeScriptsend-template.ts
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 Bulk Template

Send personalized templated emails to multiple recipients (up to 50 per call). Each recipient can have unique template data merged with default values.

Method
email.sendBulkTemplate(params: SendBulkTemplateParams): Promise<SendBulkTemplateResult>
TypeScriptsend-bulk-template.ts
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}`);  }});

Inbox (Inbound Emails)

Read, reply to, and forward inbound emails. Requires inbound email infrastructure to be deployed with wraps email inbound init.

Initialize with Inbox

TypeScriptinbox-init.ts
const email = new WrapsEmail({  inboundBucket: 'your-inbound-bucket-name',});

List Inbound Emails

TypeScriptinbox-list.ts
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

TypeScriptinbox-get.ts
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

TypeScriptinbox-reply.ts
// 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

TypeScriptinbox-forward.ts
// 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

TypeScriptinbox-attachment.ts
// 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

TypeScriptinbox-delete.ts
// Delete email and all associated files (parsed JSON, raw MIME, attachments)await email.inbox.delete('email-abc123');

Available Methods

MethodDescription
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

Email Events

Track the delivery lifecycle of every email. Requires event tracking infrastructure deployed with wraps email init (Production or Enterprise preset).

Initialize with Events

TypeScriptevents-init.ts
const email = new WrapsEmail({  historyTableName: 'wraps-email-history',});

Get Email Status

TypeScriptevents-get.ts
// 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

TypeScriptevents-list.ts
// 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 Values

StatusDescription
sentEmail accepted by SES
deliveredEmail delivered to recipient's mail server
openedRecipient opened the email (tracking pixel)
clickedRecipient clicked a link in the email
bouncedEmail bounced (hard or soft bounce)
complainedRecipient marked as spam
suppressedEmail suppressed by SES (on suppression list)

Negative statuses (bounced, complained, suppressed) always override positive ones. Status is derived from the highest-priority event.

Available Methods

MethodDescription
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

Suppression List

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

TypeScriptsuppression-get.ts
// 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

TypeScriptsuppression-add.ts
// 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

TypeScriptsuppression-remove.ts
// Remove from suppression list (idempotent — won't throw if not found)await email.suppression.remove('user@example.com');

List Suppressed Emails

TypeScriptsuppression-list.ts
// 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,  });}

Available Methods

MethodDescription
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

Error Handling

The SDK throws typed errors for different failure scenarios.

ErrorDescription
ValidationErrorInvalid input parameters (includes field property)
SESErrorAWS SES error (includes code, requestId, retryable properties)
DynamoDBErrorDynamoDB error from events API (includes code, requestId, retryable properties)
TypeScripterror-handling.ts
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);  }}

TypeScript Support

The SDK is written in TypeScript and provides full type safety out of the box.

TypeScripttyped-usage.ts
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);

SDK Defaults & Limits

Important defaults and limits to keep in mind:

  • No automatic retry on failure — implement your own retry logic if retryable is true
  • Default pagination: 20 items per page
  • Presigned URL expiry: 1 hour (for attachments)
  • Bulk send limit: 50 destinations per call
  • Attachment limit: 100 per email (10 MB total message size)

Next Steps

View on npm

Check out the package on npm for the latest version and changelog.

View Package
View on GitHub

Explore the source code, report issues, or contribute.

View Source
React Email

Learn how to build beautiful email templates with React.

Learn More