# Wraps > Deploy email, SMS, and CDN infrastructure to your AWS account with one command. > Modern DX. AWS economics. Full ownership. Wraps is a CLI and TypeScript SDK that deploys production-ready AWS infrastructure (SES, Lambda, DynamoDB, EventBridge, CloudFront) to your account. Zero credentials stored. OIDC authentication. You own everything. ## What Wraps Does ### CLI (`@wraps.dev/cli`) One command deploys complete infrastructure to your AWS account: ```bash npx @wraps.dev/cli email init # Email (SES + event processing) npx @wraps.dev/cli sms init # SMS (End User Messaging) npx @wraps.dev/cli cdn init # CDN (S3 + CloudFront) ``` **Email deploys:** AWS SES with DKIM, Lambda event processing, DynamoDB history, EventBridge + SQS event capture, IAM roles with OIDC. **SMS deploys:** AWS End User Messaging with phone number provisioning, opt-out list, event tracking, IAM roles. **CDN deploys:** S3 bucket with CloudFront distribution, optional custom domain with ACM certificate. ### TypeScript SDKs ```typescript // @wraps.dev/email import { WrapsEmail } from '@wraps.dev/email'; const email = new WrapsEmail(); await email.send({ from: 'hello@yourapp.com', to: 'user@example.com', subject: 'Welcome!', html: '

Hello from Wraps!

', }); ``` ```typescript // @wraps.dev/sms import { WrapsSMS } from '@wraps.dev/sms'; const sms = new WrapsSMS(); await sms.send({ to: '+14155551234', message: 'Your code is 123456', }); ``` ### Platform (app.wraps.dev) Visual dashboard with progressive feature unlocks: **Starter ($10/mo):** Templates with AI generation, batch sending, analytics, 30-day history, 5 workflows **Growth ($49/mo):** Topics (subscription management), Segments, Campaigns, 25 workflows **Scale ($149/mo):** Unlimited workflows, event tracking, advanced segments, multi-tenant orchestration ## Key Differentiators - **Your AWS Account**: Infrastructure deploys to your account, not ours - **No Vendor Lock-in**: Your infrastructure keeps working if you stop paying - **AWS Prices**: Pay AWS directly ($0.10/1K emails, ~$0.008/SMS segment), no markup - **Under 2 Minutes**: `npx @wraps.dev/cli email init` and done - **Unlimited Contacts**: Gate on events (what costs money), not vanity metrics - **Zero Credentials Stored**: OIDC authentication, no AWS secrets ## Installation ```bash # Deploy infrastructure (no install needed) npx @wraps.dev/cli email init # Install SDKs npm install @wraps.dev/email npm install @wraps.dev/sms ``` ## CLI Command Reference ### Email Service ``` wraps email init Deploy email infrastructure wraps email connect Connect to existing SES wraps email status Show infrastructure details wraps email check [domain] Deliverability audit (DKIM, SPF, DMARC, blacklists, TLS) wraps email verify Verify domain DNS records wraps email config Apply CLI updates to infrastructure wraps email upgrade Add features (tracking, history, dedicated IP) wraps email restore Restore from saved metadata wraps email destroy Remove all email infrastructure ``` ### Email Domains ``` wraps email domains add Add domain to SES (configures DKIM) wraps email domains list List all domains with status wraps email domains get-dkim Get DKIM tokens for DNS wraps email domains verify Verify DKIM, SPF, DMARC wraps email domains remove Remove domain from SES ``` ### Email Inbound ``` wraps email inbound init Enable inbound email receiving wraps email inbound status Show inbound status wraps email inbound verify Verify inbound DNS records wraps email inbound test Send test and verify receipt wraps email inbound destroy Remove inbound infrastructure ``` ### SMS Service ``` wraps sms init Deploy SMS infrastructure wraps sms status Show infrastructure details wraps sms test Send a test SMS wraps sms verify-number Verify destination phone number (sandbox) wraps sms upgrade Upgrade SMS features wraps sms register Register toll-free number wraps sms sync Sync infrastructure wraps sms destroy Remove SMS infrastructure ``` ### CDN Service ``` wraps cdn init Deploy CDN (S3 + CloudFront) wraps cdn status Show infrastructure details wraps cdn verify Check DNS and certificate status wraps cdn upgrade Add custom domain wraps cdn sync Sync infrastructure wraps cdn destroy Remove CDN infrastructure ``` ### AWS & Global ``` wraps aws setup Interactive AWS credential wizard wraps aws doctor Diagnose AWS configuration wraps status Overview of all services wraps destroy Remove all infrastructure wraps console Start local web dashboard wraps permissions Show required IAM permissions wraps platform connect Connect to Wraps Platform ``` ### Auth Commands ``` wraps auth login Authenticate with Wraps Platform (device flow) wraps auth status Show authentication status wraps auth logout Clear stored credentials ``` ### Platform Commands ``` wraps platform connect Link AWS account to Wraps Platform wraps platform update-role Update IAM role for Platform access ``` ### Email Templates & Workflows ``` wraps email templates init Initialize templates-as-code directory wraps email templates preview Start preview server with hot-reload wraps email templates push Compile and push templates to SES + dashboard wraps email workflows validate Validate workflow definitions wraps email workflows push Push workflows to Wraps Platform wraps email test Send test email via SES simulator wraps email check [domain] Deliverability audit (DKIM, SPF, DMARC, blacklists) ``` ### Common Flags ``` -p, --provider Hosting provider (vercel, aws, railway, other) -r, --region AWS region -d, --domain Domain name -y, --yes Skip confirmation (non-destructive ops) -f, --force Force without confirmation (destructive ops) --preset Config preset (starter, production, enterprise, custom) --preview Preview changes without deploying --json JSON output (where supported) --account Target specific AWS account --token API token for authentication --verbose Enable verbose output ``` ## Email SDK Reference (@wraps.dev/email) ### Constructor ```typescript import { WrapsEmail } from '@wraps.dev/email'; const email = new WrapsEmail({ region?: string, // AWS region (default: us-east-1) credentials?: // Static credentials or provider function | { accessKeyId: string, secretAccessKey: string, sessionToken?: string } | AwsCredentialIdentityProvider, roleArn?: string, // IAM role for OIDC roleSessionName?: string, // Session name for AssumeRole (default: 'wraps-email-session') endpoint?: string, // Custom endpoint (LocalStack) client?: SESClient, // Pre-configured SES client (overrides region/credentials/roleArn) s3Client?: S3Client, // Pre-configured S3 client for inbox sesv2Client?: SESv2Client, // Pre-configured SES v2 client for suppression/batch dynamodbClient?: DynamoDBDocumentClient, // Pre-configured DynamoDB client for events inboxBucketName?: string, // S3 bucket for inbound email (enables email.inbox) historyTableName?: string, // DynamoDB table for events (enables email.events) }); ``` ### email.send(params) ```typescript await email.send({ from: 'you@company.com', // Required to: 'user@example.com', // Required (string or string[]) subject: 'Subject', // Required html: '

Hello

', // html, text, or react required text: 'Hello', // Plain text fallback react: , // React.email component cc: ['cc@example.com'], // Optional bcc: ['bcc@example.com'], // Optional replyTo: ['reply@example.com'], // Optional tags: { campaign: 'welcome' }, // Optional (SES tags) attachments: [{ // Optional filename: 'file.pdf', content: Buffer | string, // Buffer or base64 contentType: 'application/pdf', // Optional, auto-detected }], }); // Returns: { messageId: string } ``` ### email.sendTemplate(params) ```typescript await email.sendTemplate({ from: 'you@company.com', to: 'user@example.com', template: 'welcome', templateData: { name: 'Alice', url: 'https://...' }, }); ``` ### email.sendBatch(params) ```typescript const result = await email.sendBatch({ from: 'you@company.com', entries: [ // Up to 100, unique content per recipient { to: 'alice@example.com', subject: 'Hi Alice', html: '

Hello Alice!

' }, { to: 'bob@example.com', subject: 'Hi Bob', html: '

Hello Bob!

' }, ], replyTo: 'support@company.com', // Optional, shared across all entries tags: { campaign: 'welcome' }, // Optional default tags (overridable per entry) }); // Returns: { results: BatchEntryResult[], successCount: number, failureCount: number } // Each result: { index, messageId?, status: 'success' | 'failure', error? } // Throws BatchError on partial failure ``` ### email.sendBulkTemplate(params) ```typescript await email.sendBulkTemplate({ from: 'you@company.com', template: 'weekly-digest', destinations: [ // Up to 50 { to: 'alice@example.com', templateData: { name: 'Alice' } }, { to: 'bob@example.com', templateData: { name: 'Bob' } }, ], }); ``` ### email.templates ```typescript await email.templates.create({ name, subject, html, text? }); await email.templates.createFromReact({ name, subject, react }); await email.templates.update({ name, subject?, html?, text? }); await email.templates.get(name); // Returns template details await email.templates.list(); // Returns template metadata[] await email.templates.delete(name); ``` ### Optional Features: Inbox, Events, Suppression Inbox and Events are enabled by constructor config. Suppression is always available. ```typescript const email = new WrapsEmail({ inboxBucketName: 'my-inbox-bucket', // enables email.inbox historyTableName: 'my-events-table', // enables email.events }); // email.inbox is null unless inboxBucketName is provided // email.events is null unless historyTableName is provided // email.suppression is always available ``` ### email.inbox (requires inboxBucketName) ```typescript if (email.inbox) { await email.inbox.list({ maxResults: 20 }); await email.inbox.get(messageId); await email.inbox.getAttachment(messageId, attachmentId); // Returns presigned URL (1hr) await email.inbox.getRaw(messageId); await email.inbox.delete(messageId); await email.inbox.forward(messageId, { to: 'other@example.com', from: 'me@example.com' }); await email.inbox.reply(messageId, { from: 'me@example.com', html: '

Reply

' }); } ``` ### email.events (requires historyTableName) ```typescript if (email.events) { await email.events.get(messageId); // Events sorted by priority await email.events.list({ accountId: '123456789012', maxResults: 50 }); } ``` ### email.suppression (always available) ```typescript await email.suppression.get(emailAddress); await email.suppression.add(emailAddress, 'BOUNCE'); await email.suppression.remove(emailAddress); await email.suppression.list({ reason: 'BOUNCE', maxResults: 100 }); ``` ### Error Types ```typescript import { ValidationError, SESError, DynamoDBError, BatchError, WrapsEmailError } from '@wraps.dev/email'; // WrapsEmailError: base class for all email errors // ValidationError: invalid input (bad email, missing fields) // .field?: string - which field failed // .message: string // SESError: AWS SES error (rate limit, unverified sender) // .code: string - 'MessageRejected', 'Throttling', etc. // .requestId: string // .retryable: boolean // DynamoDBError: email history read/write error // .code: string, .requestId: string, .retryable: boolean // BatchError: partial failure in sendBatch() // .results: BatchEntryResult[] - per-entry results // .successCount: number // .failureCount: number ``` ### SDK Defaults & Limits - No automatic retry (implement your own if retryable is true) - Pagination: 20 items per page default - Presigned URL expiry: 1 hour - Bulk send: 50 destinations max per call - Attachments: 100 max, 10MB total ## SMS SDK Reference (@wraps.dev/sms) ### Constructor ```typescript import { WrapsSMS } from '@wraps.dev/sms'; const sms = new WrapsSMS({ region?: string, credentials?: { accessKeyId, secretAccessKey }, roleArn?: string, }); ``` ### sms.send(params) ```typescript const result = await sms.send({ to: '+14155551234', // Required (E.164) message: 'Hello!', // Required messageType?: 'TRANSACTIONAL', // TRANSACTIONAL or PROMOTIONAL from?: '+18005551234', // Override sender context?: { userId: '123' }, // Custom metadata dryRun?: true, // Validate without sending }); // Returns: { messageId, status, to, from, segments } ``` ### sms.sendBatch(params) ```typescript const result = await sms.sendBatch({ messages: [ { to: '+14155551234', message: 'Hello Alice!' }, { to: '+14155555678', message: 'Hello Bob!' }, ], messageType: 'TRANSACTIONAL', }); // Returns: { batchId, total, queued, failed, results[] } ``` ### sms.numbers.list() ```typescript const numbers = await sms.numbers.list(); // Returns: [{ phoneNumberId, phoneNumber, numberType, messageType, twoWayEnabled }] ``` ### sms.optOuts ```typescript await sms.optOuts.check('+14155551234'); // Returns boolean await sms.optOuts.add('+14155551234'); await sms.optOuts.remove('+14155551234'); ``` ### Utilities ```typescript import { calculateSegments, validatePhoneNumber } from '@wraps.dev/sms'; calculateSegments('Hello!'); // 1 calculateSegments('a'.repeat(200)); // 2 validatePhoneNumber('+14155551234', 'to'); // Throws if invalid ``` ### sms.numbers.get(phoneNumberId) ```typescript const number = await sms.numbers.get('phone-number-id'); // Returns: { phoneNumberId, phoneNumber, numberType, messageType, twoWayEnabled } ``` ### sms.optOuts.list(options?) ```typescript const result = await sms.optOuts.list({ limit: 20 }); // Returns: { entries: OptOutEntry[], nextToken?: string } ``` ### Error Types ```typescript import { SMSError, ValidationError, OptedOutError, RateLimitError } from '@wraps.dev/sms'; // ValidationError: invalid input (.field, .message) // OptedOutError: user opted out (.phoneNumber) // SMSError: AWS error (.code, .retryable) // RateLimitError: rate limit exceeded (.retryAfter in seconds) ``` ### SMS Utilities ```typescript import { sanitizePhoneNumber } from '@wraps.dev/sms'; sanitizePhoneNumber('(415) 555-1234'); // '+14155551234' ``` Note: Default messageType is TRANSACTIONAL. Batch sends are sequential (not parallel). ## Platform SDK Reference (@wraps.dev/client) ### defineConfig() ```typescript import { defineConfig } from '@wraps.dev/client'; export default defineConfig({ org: 'your-org-slug', from: 'hello@yourdomain.com', replyTo: 'support@yourdomain.com', region: 'us-east-1', environments: { staging: { from: 'staging@yourdomain.com' } }, defaultEnv: 'production', templatesDir: './templates', workflowsDir: './workflows', brandFile: './brand.ts', preview: { port: 3333 }, }); ``` ### defineBrand() ```typescript import { defineBrand } from '@wraps.dev/client'; export default defineBrand({ companyName: 'Your Company', colors: { primary: '#6366f1', secondary: '#a5b4fc', background: '#ffffff', text: '#1f2937', muted: '#9ca3af' }, fonts: { heading: 'Inter, sans-serif', body: 'Inter, sans-serif' }, buttonStyle: { borderRadius: '6px', padding: '12px 24px' }, logoUrl: 'https://yourdomain.com/logo.png', address: '123 Main St, San Francisco, CA 94102', socialLinks: { twitter: 'https://twitter.com/yourcompany' }, }); ``` ### defineWorkflow() ```typescript import { defineWorkflow, sendEmail, delay, condition, exit } from '@wraps.dev/client'; export default defineWorkflow({ name: 'Welcome Sequence', trigger: { type: 'contact_created' }, settings: { allowReentry: false, contactCooldownSeconds: 86400 }, steps: [ sendEmail('welcome', { template: 'welcome-email' }), delay('wait', { days: 1 }), condition('check', { field: 'contact.hasActivated', operator: 'equals', value: true, branches: { yes: [exit('done')], no: [sendEmail('reminder', { template: 'activate' })] } }), ], }); ``` ### Workflow Step Helpers - `sendEmail(id, { template, from?, fromName?, replyTo? })` - Send email - `sendSms(id, { template?, message?, senderId? })` - Send SMS - `delay(id, { days?, hours?, minutes? })` - Wait - `condition(id, { field, operator, value, branches })` - Branch - `waitForEvent(id, { eventName, timeout? })` - Wait for event - `waitForEmailEngagement(id, { emailStepId, engagementType, timeout? })` - Wait for open/click - `exit(id, { reason?, markAs? })` - End workflow - `updateContact(id, { updates })` - Modify contact fields (set, increment, decrement, append, remove) - `subscribeTopic(id, { topicId, channel? })` - Subscribe to topic - `unsubscribeTopic(id, { topicId, channel? })` - Unsubscribe from topic - `webhook(id, { url, method?, headers?, body? })` - External webhook - `cascade(id, { channels })` - Cross-channel cascade (see below) ### Trigger Types - `contact_created` - New contact added - `contact_updated` - Contact fields changed - `event` - Custom event received (requires `eventName`) - `segment_entry` - Contact enters segment (requires `segmentId`) - `segment_exit` - Contact exits segment (requires `segmentId`) - `schedule` - Cron-based schedule (requires `schedule` cron expression, optional `timezone`) - `api` - Triggered via API call - `topic_subscribed` - Contact subscribes to topic (requires `topicId`) - `topic_unsubscribed` - Contact unsubscribes from topic (requires `topicId`) ### Workflow Settings ```typescript settings?: { allowReentry?: boolean, // Allow same contact to re-enter reentryDelaySeconds?: number, // Min seconds between re-entries maxConcurrentExecutions?: number, // Max parallel executions contactCooldownSeconds?: number, // Cooldown before re-trigger } ``` ### cascade() — Cross-Channel Orchestration Tries channels in order; stops when engagement is detected. Returns `StepDefinition[]`. ```typescript import { defineWorkflow, cascade, sendEmail } from '@wraps.dev/client'; export default defineWorkflow({ name: 'Notify User', trigger: { type: 'event', eventName: 'payment_failed' }, steps: [ ...cascade('notify', { channels: [ { type: 'email', template: 'payment-failed', waitFor: { hours: 2 }, engagement: 'opened' }, { type: 'sms', template: 'payment-failed-sms' }, ], }), ], }); // Expands to: sendEmail → waitForEmailEngagement → condition (engaged?) → yes: exit → no: sendSms ``` ## Authentication All SDKs follow a 4-step credential resolution chain (in priority order): ### 1. Pre-configured AWS client (highest priority) ```typescript import { SESClient } from '@aws-sdk/client-ses'; const sesClient = new SESClient({ /* custom config */ }); const email = new WrapsEmail({ client: sesClient }); // Bypasses all other credential resolution ``` ### 2. OIDC role assumption (Vercel, EKS, GitHub Actions) ```typescript const email = new WrapsEmail({ roleArn: process.env.AWS_ROLE_ARN, roleSessionName: 'my-app-session', // Optional, defaults to 'wraps-email-session' }); // Uses AssumeRoleWithWebIdentity via AWS_WEB_IDENTITY_TOKEN_FILE ``` ### 3. Explicit credentials (static or provider function) ```typescript // Static credentials const email = new WrapsEmail({ credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID!, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!, }, region: 'us-east-1', }); // Credential provider function (e.g., from Vercel OIDC helper) const email = new WrapsEmail({ credentials: myCredentialProvider, // AwsCredentialIdentityProvider }); ``` ### 4. AWS SDK default chain (lowest priority, recommended) ```typescript const email = new WrapsEmail(); // Resolves: env vars -> ~/.aws/credentials -> ECS container -> EC2 instance metadata ``` ## Common Workflows ### 1. Deploy email and send first message ```bash npx @wraps.dev/cli email init npx @wraps.dev/cli email domains add -d yourapp.com npx @wraps.dev/cli email domains get-dkim -d yourapp.com # Add CNAME records to DNS provider npx @wraps.dev/cli email domains verify -d yourapp.com npm install @wraps.dev/email ``` ### 2. Deploy SMS and send first message ```bash npx @wraps.dev/cli sms init npx @wraps.dev/cli sms verify-number --phoneNumber +14155551234 npx @wraps.dev/cli sms test --to +14155551234 --message "Hello!" npm install @wraps.dev/sms ``` ### 3. Run deliverability audit ```bash npx @wraps.dev/cli email check yourapp.com # Checks DKIM, SPF, DMARC, MX TLS, blacklists ``` ### 4. Connect existing SES and upgrade ```bash npx @wraps.dev/cli email connect npx @wraps.dev/cli email upgrade ``` ## Configuration Presets ### Email | Preset | Monthly Cost | Features | |--------|-------------|----------| | starter | ~$0.05 | Open/click tracking, bounce/complaint suppression | | production | ~$2-5 | + Real-time event tracking, 90-day history, reputation metrics | | enterprise | ~$50-100 | + Dedicated IP, 1-year history, all 10 SES event types | ### SMS | Preset | Monthly Cost | Features | |--------|-------------|----------| | starter | ~$1 | Simulator phone number | | production | ~$2-10 | Toll-free number, event tracking | | enterprise | ~$10-50 | Full features, link tracking | ## Pricing | | Free | Starter | Growth | Scale | |--|------|---------|--------|-------| | **Price** | $0 | $10/mo | $49/mo | $149/mo | | **Dashboard** | Self-host | Hosted | Hosted | Hosted | | **Contacts** | Unlimited | Unlimited | Unlimited | Unlimited | | **Workflows** | -- | 5 | 25 | Unlimited | | **Features** | CLI/SDK | + Templates, Analytics | + Topics, Segments, Campaigns | + Events, Advanced Segments | You pay Wraps for tooling. You pay AWS for infrastructure. No surprises. **Example**: 100K emails/month = $10-49 (Wraps) + ~$10 (AWS) = $20-59 total. Managed services charge $80-150+ for the same volume. ## Available Packages - `@wraps.dev/email` - Email sending via AWS SES - `@wraps.dev/sms` - SMS via AWS End User Messaging - `@wraps.dev/cdn` - Asset delivery via AWS CloudFront - `@wraps.dev/cli` - Infrastructure deployment CLI ## Technical Stack - Infrastructure: Pulumi deploying to AWS (SES, DynamoDB, Lambda, EventBridge, SQS, CloudFront, S3) - SDK: TypeScript, works with any Node.js framework - Platform: Next.js 15, React 19, shadcn/ui, Elysia API - Database: PostgreSQL (Neon) + Drizzle ORM - Auth: Better Auth with OIDC (Vercel, AWS native) ## Use Cases - Transactional emails (welcome, password reset, receipts) - Marketing broadcasts with segmentation - Automated email workflows (drip campaigns, onboarding) - SMS verification codes and notifications - CDN for email assets and hosted images - Developer applications needing full AWS control ## Environment Variables ### Wraps Configuration - `WRAPS_LOCAL_ONLY` - Disable telemetry and API calls (default: false) - `WRAPS_API_KEY` - API key for Wraps Platform - `WRAPS_API_URL` - Custom API endpoint (default: https://api.wraps.dev) - `WRAPS_TELEMETRY_DISABLED` - Disable anonymous telemetry - `WRAPS_HOME` - Custom config directory (default: ~/.wraps) ### AWS Credentials - `AWS_ACCESS_KEY_ID` - AWS access key - `AWS_SECRET_ACCESS_KEY` - AWS secret key - `AWS_SESSION_TOKEN` - Temporary session token - `AWS_REGION` - AWS region (default: us-east-1) - `AWS_PROFILE` - Named AWS CLI profile - `AWS_ROLE_ARN` - IAM role ARN for OIDC assumption ### DNS Automation - `CLOUDFLARE_API_TOKEN` - Cloudflare API token - `CLOUDFLARE_ZONE_ID` - Cloudflare zone ID - `VERCEL_TOKEN` - Vercel API token for DNS ## Infrastructure: What Gets Deployed ### Email (wraps email init) **All presets:** IAM role (OIDC trust), SES configuration set, email identity with DKIM **Production preset adds:** EventBridge rule, SQS queue + DLQ, Lambda processor (Node.js 20, 128MB), DynamoDB table (90-day TTL) **Enterprise preset adds:** Dedicated IP address, 365-day history, all 10 SES event types Architecture: SES → EventBridge → SQS + DLQ → Lambda → DynamoDB All resources tagged with ManagedBy: 'wraps-cli', prefixed with 'wraps-email-' ### SMS (wraps sms init) IAM role, phone number (simulator or toll-free), opt-out list, event tracking ### CDN (wraps cdn init) S3 bucket (private, versioned), CloudFront distribution (OAI, HTTPS), optional ACM certificate + custom domain ## Links - Website: https://wraps.dev - Quickstart: https://wraps.dev/docs/quickstart/email - Next.js Guide: https://wraps.dev/docs/quickstart/email/nextjs - Express Guide: https://wraps.dev/docs/quickstart/email/express - Remix Guide: https://wraps.dev/docs/quickstart/email/remix - Email SDK Reference: https://wraps.dev/docs/sdk-reference - SMS SDK Reference: https://wraps.dev/docs/sms-sdk-reference - Platform SDK Reference: https://wraps.dev/docs/client-sdk-reference - CLI Reference: https://wraps.dev/docs/cli-reference - Auth Commands: https://wraps.dev/docs/cli-reference/auth - AWS Commands: https://wraps.dev/docs/cli-reference/aws - Platform Commands: https://wraps.dev/docs/cli-reference/platform - Infrastructure (Email): https://wraps.dev/docs/infrastructure/email - Infrastructure (SMS): https://wraps.dev/docs/infrastructure/sms - Infrastructure (CDN): https://wraps.dev/docs/infrastructure/cdn - Error Codes: https://wraps.dev/docs/reference/errors - Environment Variables: https://wraps.dev/docs/reference/environment-variables - Configuration Presets: https://wraps.dev/docs/guides/configuration-presets - Templates Guide: https://wraps.dev/docs/guides/templates - Workflows Guide: https://wraps.dev/docs/guides/workflows - Orchestration Guide: https://wraps.dev/docs/guides/orchestration - Webhooks Guide: https://wraps.dev/docs/guides/webhooks - GitHub (SDK): https://github.com/wraps-team/wraps-js - npm (email): https://npmjs.com/package/@wraps.dev/email - npm (sms): https://npmjs.com/package/@wraps.dev/sms - npm (client): https://npmjs.com/package/@wraps.dev/client - npm (cli): https://npmjs.com/package/@wraps.dev/cli ## Contact - Support: support@wraps.dev - Twitter: @useWraps