Get started with Wraps email infrastructure. Guides through deploying infrastructure, verifying a domain, installing the SDK, and sending the first email...
Zero-to-sending setup guide for Wraps email infrastructure. Detects project context and adapts instructions to the user's framework and hosting provider.
Walk the user through the full setup flow. Don't dump everything at once. Progress step by step, adapting to what you detect in their project.
Before starting, understand the project:
# Framework detection
cat package.json | grep -E '"(next|express|fastify|remix|astro|hono|nuxt)"'
# Hosting detection
ls vercel.json railway.json Dockerfile fly.toml render.yaml 2>/dev/null
# Existing email setup
cat package.json | grep -E '"(nodemailer|@sendgrid|resend|postmark|@aws-sdk/client-ses)"'
# AWS credentials
aws sts get-caller-identity
Adapt guidance based on findings:
aws configure firstAn AWS account with credentials configured. Verify:
aws sts get-caller-identity
If this fails, the user needs to configure AWS credentials:
aws configure with access key/secretaws sso loginAWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEYNode.js 20 or higher:
node --version
npx @wraps.dev/cli@latest email init
The command prompts for:
What gets deployed to the user's AWS account:
For Vercel users: OIDC provider is set up automatically. No AWS credentials need to be stored as environment variables.
After email init, the CLI outputs DNS records to add. Three types:
Add 3 CNAME records provided by the CLI. Example format:
selector1._domainkey.yourapp.com CNAME selector1.dkim.amazonses.com
selector2._domainkey.yourapp.com CNAME selector2.dkim.amazonses.com
selector3._domainkey.yourapp.com CNAME selector3.dkim.amazonses.com
_dmarc.yourapp.com TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@yourapp.com"
DNS propagation takes 5-60 minutes. Check with:
npx @wraps.dev/cli@latest email verify --domain yourapp.com
Or check DKIM tokens directly:
npx @wraps.dev/cli@latest email domains get-dkim --domain yourapp.com
# npm
npm install @wraps.dev/email
# pnpm
pnpm add @wraps.dev/email
# yarn
yarn add @wraps.dev/email
'use server';
import { WrapsEmail } from '@wraps.dev/email';
const email = new WrapsEmail();
export async function sendWelcomeEmail(to: string, name: string) {
const result = await email.send({
from: 'hello@yourapp.com',
to,
subject: `Welcome, ${name}!`,
html: `<h1>Welcome to our app, ${name}!</h1><p>We're glad you're here.</p>`,
});
if (!result.success) {
throw new Error(`Failed to send: ${result.error.message}`);
}
return result.data.messageId;
}
import { WrapsEmail } from '@wraps.dev/email';
import { NextResponse } from 'next/server';
const email = new WrapsEmail();
export async function POST(request: Request) {
const { to, name } = await request.json();
const result = await email.send({
from: 'hello@yourapp.com',
to,
subject: `Welcome, ${name}!`,
html: `<h1>Welcome, ${name}!</h1>`,
});
if (!result.success) {
return NextResponse.json({ error: result.error.message }, { status: 500 });
}
return NextResponse.json({ messageId: result.data.messageId });
}
import { WrapsEmail } from '@wraps.dev/email';
const email = new WrapsEmail();
app.post('/api/send-email', async (req, res) => {
const { to, name } = req.body;
const result = await email.send({
from: 'hello@yourapp.com',
to,
subject: `Welcome, ${name}!`,
html: `<h1>Welcome, ${name}!</h1>`,
});
if (!result.success) {
return res.status(500).json({ error: result.error.message });
}
res.json({ messageId: result.data.messageId });
});
import { WrapsEmail } from '@wraps.dev/email';
const email = new WrapsEmail();
const result = await email.send({
from: 'hello@yourapp.com',
to: 'test@example.com',
subject: 'Test from Wraps',
html: '<h1>It works!</h1>',
});
console.log(result.success ? `Sent: ${result.data.messageId}` : `Error: ${result.error.message}`);
For Vercel deployments, the SDK automatically uses OIDC. No environment variables needed:
const email = new WrapsEmail(); // Auto-detects Vercel OIDC
For non-Vercel environments, configure credentials:
const email = new WrapsEmail({
region: 'us-east-1',
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
});
npx @wraps.dev/cli@latest email status
aws sesv2 get-account --region us-east-1 --query 'SendQuota'
Based on the project context, suggest relevant next steps:
For SaaS apps:
@wraps.dev/email template APIFor marketing / newsletters:
For AI-powered products:
For all projects:
New AWS accounts start in sandbox (200 emails/day, verified recipients only). Request production access:
DKIM records can take up to 72 hours in rare cases (usually 5-60 minutes). Use wraps email verify to check status.
Vercel OIDC authentication requires a Vercel team account (not personal). For personal accounts, use environment variable credentials instead.
// Before (Resend)
import { Resend } from 'resend';
const resend = new Resend('re_xxx');
await resend.emails.send({ from: '...', to: '...', subject: '...', html: '...' });
// After (Wraps)
import { WrapsEmail } from '@wraps.dev/email';
const email = new WrapsEmail();
await email.send({ from: '...', to: '...', subject: '...', html: '...' });
Key differences: No API key needed (uses AWS IAM). Infrastructure in YOUR AWS account.
// Before (SendGrid)
import sgMail from '@sendgrid/mail';
sgMail.setApiKey(process.env.SENDGRID_API_KEY!);
await sgMail.send({ from: '...', to: '...', subject: '...', html: '...' });
// After (Wraps)
import { WrapsEmail } from '@wraps.dev/email';
const email = new WrapsEmail();
await email.send({ from: '...', to: '...', subject: '...', html: '...' });
// Before (Nodemailer)
import nodemailer from 'nodemailer';
const transport = nodemailer.createTransport({ SES: new AWS.SES() });
await transport.sendMail({ from: '...', to: '...', subject: '...', html: '...' });
// After (Wraps)
import { WrapsEmail } from '@wraps.dev/email';
const email = new WrapsEmail();
await email.send({ from: '...', to: '...', subject: '...', html: '...' });
Wraps adds: event tracking, analytics dashboard, contact management, templates, and workflows on top of the same SES infrastructure.
When switching from another provider:
amazonses.com (if not already present)