wraps-email

wraps-email

TypeScript SDK for AWS SES with automatic credential resolution, React.email support, and template management.

1estrellas
0forks
Actualizado 1/20/2026
SKILL.md
readonlyread-only
name
wraps-email
description

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 init for easy setup)
  • Verified domain or email address in SES

You Might Also Like

Related Skills

gog

gog

169Kdev-api

Google Workspace CLI for Gmail, Calendar, Drive, Contacts, Sheets, and Docs.

openclaw avataropenclaw
Obtener
weather

weather

169Kdev-api

Get current weather and forecasts (no API key required).

openclaw avataropenclaw
Obtener

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 avatarlanggenius
Obtener
blucli

blucli

92Kdev-api

BluOS CLI (blu) for discovery, playback, grouping, and volume.

moltbot avatarmoltbot
Obtener
ordercli

ordercli

92Kdev-api

Foodora-only CLI for checking past orders and active order status (Deliveroo WIP).

moltbot avatarmoltbot
Obtener
gifgrep

gifgrep

92Kdev-api

Search GIF providers with CLI/TUI, download results, and extract stills/sheets.

moltbot avatarmoltbot
Obtener