원클릭으로
wraps-sms
TypeScript SDK for AWS End User Messaging (Pinpoint SMS) with opt-out management, batch sending, and E.164 validation.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
TypeScript SDK for AWS End User Messaging (Pinpoint SMS) with opt-out management, batch sending, and E.164 validation.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
CLI that deploys SES, DynamoDB, Lambda, and IAM resources to your AWS account with automatic DKIM, SPF, DMARC configuration.
Email deliverability, warming, compliance, and operational guidelines for AWS Simple Email Service.
Best practices for email content that avoids spam filters. Apply these rules when writing email copy, building templates, or debugging spam placement issues.
Interactive SES deliverability troubleshooting. Diagnoses bounces, spam placement, send failures, account health, and complaint issues with targeted AWS CLI commands and ordered remediation steps.
TypeScript SDK for AWS SES with automatic credential resolution, React.email support, and template management.
Get started with Wraps email infrastructure. Guides through deploying infrastructure, verifying a domain, installing the SDK, and sending the first email. Adapts to project framework and hosting provider.
| name | wraps-sms |
| description | TypeScript SDK for AWS End User Messaging (Pinpoint SMS) with opt-out management, batch sending, and E.164 validation. |
TypeScript SDK for AWS End User Messaging (Pinpoint SMS) with opt-out management, batch sending, and E.164 validation. Calls your AWS account directly — no proxy, no markup.
npm install @wraps.dev/sms
# or
pnpm add @wraps.dev/sms
import { WrapsSMS } from '@wraps.dev/sms';
const sms = new WrapsSMS();
const result = await sms.send({
to: '+14155551234',
message: 'Your verification code is 123456',
});
console.log('Sent:', result.messageId);
// Uses AWS credential chain (env vars, IAM role, ~/.aws/credentials)
const sms = new WrapsSMS();
const sms = new WrapsSMS({
region: 'us-west-2', // defaults to us-east-1
});
const sms = new WrapsSMS({
region: 'us-east-1',
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
});
// For Vercel, EKS, GitHub Actions with OIDC federation
const sms = new WrapsSMS({
region: 'us-east-1',
roleArn: 'arn:aws:iam::123456789012:role/WrapsSMSRole',
roleSessionName: 'my-app-session', // optional
});
import { fromWebToken } from '@aws-sdk/credential-providers';
const credentials = fromWebToken({
roleArn: process.env.AWS_ROLE_ARN!,
webIdentityToken: async () => process.env.VERCEL_OIDC_TOKEN!,
});
const sms = new WrapsSMS({
region: 'us-east-1',
credentials,
});
const result = await sms.send({
to: '+14155551234', // E.164 format required
message: 'Hello from Wraps!',
});
console.log('Message ID:', result.messageId);
console.log('Segments:', result.segments);
console.log('Status:', result.status);
// OTP, alerts, notifications (higher priority, opt-out not required)
await sms.send({
to: '+14155551234',
message: 'Your code is 123456',
messageType: 'TRANSACTIONAL', // default
});
// Marketing messages (requires opt-in, subject to quiet hours)
await sms.send({
to: '+14155551234',
message: 'Sale! 20% off today only!',
messageType: 'PROMOTIONAL',
});
// Use a specific origination number from your account
await sms.send({
to: '+14155551234',
message: 'Hello!',
from: '+18005551234', // Your registered number
});
await sms.send({
to: '+14155551234',
message: 'Your order has shipped!',
context: {
orderId: 'order_123',
userId: 'user_456',
type: 'shipping_notification',
},
});
// Fail if message would cost more than specified amount
await sms.send({
to: '+14155551234',
message: 'Hello!',
maxPrice: '0.05', // USD per segment
});
// Message expires if not delivered within TTL
await sms.send({
to: '+14155551234',
message: 'Your OTP is 123456',
ttl: 300, // 5 minutes in seconds
});
const result = await sms.send({
to: '+14155551234',
message: 'Test message',
dryRun: true,
});
console.log('Would use', result.segments, 'segment(s)');
// No message is actually sent
const result = await sms.sendBatch({
messages: [
{ to: '+14155551234', message: 'Hello Alice!' },
{ to: '+14155555678', message: 'Hello Bob!' },
{ to: '+14155559012', message: 'Hello Carol!' },
],
messageType: 'TRANSACTIONAL',
});
console.log(`Total: ${result.total}`);
console.log(`Queued: ${result.queued}`);
console.log(`Failed: ${result.failed}`);
// Check individual results
result.results.forEach((r) => {
if (r.status === 'QUEUED') {
console.log(`${r.to}: ${r.messageId}`);
} else {
console.log(`${r.to}: FAILED - ${r.error}`);
}
});
const numbers = await sms.numbers.list();
numbers.forEach((n) => {
console.log(`${n.phoneNumber} (${n.numberType})`);
console.log(` Message type: ${n.messageType}`);
console.log(` Two-way enabled: ${n.twoWayEnabled}`);
console.log(` Country: ${n.isoCountryCode}`);
});
const number = await sms.numbers.get('phone-number-id-123');
if (number) {
console.log(`Phone: ${number.phoneNumber}`);
console.log(`Type: ${number.numberType}`);
}
TCPA compliance requires honoring opt-out requests.
const isOptedOut = await sms.optOuts.check('+14155551234');
if (isOptedOut) {
console.log('User has opted out, do not send');
} else {
await sms.send({
to: '+14155551234',
message: 'Hello!',
});
}
const optOuts = await sms.optOuts.list();
optOuts.forEach((entry) => {
console.log(`${entry.phoneNumber} opted out at ${entry.optedOutAt}`);
});
// User requested to stop receiving messages
await sms.optOuts.add('+14155551234');
// User re-subscribed (must have explicit consent)
await sms.optOuts.remove('+14155551234');
import { WrapsSMS, SMSError, ValidationError, OptedOutError } from '@wraps.dev/sms';
try {
await sms.send({
to: '+14155551234',
message: 'Hello!',
});
} catch (error) {
if (error instanceof ValidationError) {
// Invalid parameters (e.g., invalid phone number format)
console.error('Validation error:', error.message);
} else if (error instanceof OptedOutError) {
// Recipient has opted out
console.error('User opted out:', error.phoneNumber);
} else if (error instanceof SMSError) {
// AWS SMS service error
console.error('SMS error:', error.message);
console.error('Error code:', error.code);
console.error('Request ID:', error.requestId);
console.error('Is throttled:', error.isThrottled);
} else {
throw error;
}
}
import { validatePhoneNumber } from '@wraps.dev/sms';
const isValid = validatePhoneNumber('+14155551234'); // true
const isInvalid = validatePhoneNumber('415-555-1234'); // false (not E.164)
import { sanitizePhoneNumber } from '@wraps.dev/sms';
const clean = sanitizePhoneNumber('(415) 555-1234', 'US');
// Returns: '+14155551234'
import { calculateSegments } from '@wraps.dev/sms';
// GSM-7 encoding: 160 chars = 1 segment
const segments1 = calculateSegments('Hello world!'); // 1
// Unicode: 70 chars = 1 segment
const segments2 = calculateSegments('Hello! emoji here'); // may be more due to encoding
// Long message
const longMsg = 'A'.repeat(200);
const segments3 = calculateSegments(longMsg); // 2 (for GSM-7)
// When done (e.g., in serverless cleanup or app shutdown)
sms.destroy();
import type {
WrapsSMSConfig,
SendOptions,
SendResult,
BatchOptions,
BatchResult,
BatchMessage,
BatchMessageResult,
PhoneNumber,
OptOutEntry,
MessageType,
MessageStatus,
} from '@wraps.dev/sms';
import { WrapsSMS } from '@wraps.dev/sms';
class OTPService {
private sms: WrapsSMS;
constructor() {
this.sms = new WrapsSMS({
region: process.env.AWS_REGION,
});
}
async sendOTP(phoneNumber: string, code: string) {
return this.sms.send({
to: phoneNumber,
message: `Your verification code is ${code}. Valid for 10 minutes.`,
messageType: 'TRANSACTIONAL',
ttl: 600, // 10 minutes
context: {
type: 'otp',
},
});
}
}
import { WrapsSMS, OptedOutError } from '@wraps.dev/sms';
class NotificationService {
private sms: WrapsSMS;
constructor() {
this.sms = new WrapsSMS();
}
async sendNotification(phoneNumber: string, message: string) {
// Check opt-out status first
const isOptedOut = await this.sms.optOuts.check(phoneNumber);
if (isOptedOut) {
console.log(`Skipping ${phoneNumber} - opted out`);
return null;
}
try {
return await this.sms.send({
to: phoneNumber,
message,
messageType: 'TRANSACTIONAL',
});
} catch (error) {
if (error instanceof OptedOutError) {
// Race condition: user opted out between check and send
console.log(`User ${phoneNumber} opted out`);
return null;
}
throw error;
}
}
}
import { WrapsSMS } from '@wraps.dev/sms';
// Initialize outside handler for connection reuse
const sms = new WrapsSMS({
roleArn: process.env.AWS_ROLE_ARN,
});
export async function POST(request: Request) {
const { to, message } = await request.json();
const result = await sms.send({
to,
message,
messageType: 'TRANSACTIONAL',
});
return Response.json({ messageId: result.messageId });
}
Always use E.164 format:
+14155551234 (not 415-555-1234)+447911123456 (not 07911 123456)+4915112345678Messages are billed per segment: