Tag Archives: messaging

Introducing Amazon Simple Email Service (SES) pricing plans

Post Syndicated from Advait Gomkale original https://aws.amazon.com/blogs/messaging-and-targeting/introducing-amazon-simple-email-service-ses-pricing-plans/

Businesses rely on email to deliver critical notifications, nurture customer relationships, and grow revenue. But their success depends on more than just sending the right message. It depends on emails getting delivered to the inbox and ultimately getting read. When emails land in spam or go unread, the business impact is real and measurable: missed engagement, eroded customer trust, and lost revenue.

Most email providers offer capabilities to help improve deliverability, including dedicated sending infrastructure, reputation monitoring, address validation, and inbox placement testing. But these capabilities are typically sold as individual add-ons, each priced separately. Reaching the inbox consistently shouldn’t require evaluating dozens of options independently.

That changes today with Amazon SES pricing plans. Pick a plan, and the right capabilities are already included at up to 22% less than purchasing them individually.

Amazon SES pricing plans

Amazon SES offers three plans: Essentials, Pro, and Enterprise. Each builds on the one before it, offering more capability, so you choose the one that fits your email needs.

Essentials

Get started with Amazon SES. Monitor how emails perform and get insights to help improve deliverability over time.

Send email reliably at scale and see what’s happening: which emails are landing, which are bouncing, and what needs attention. Essentials gives you the data and recommendations to guide your improvements, with the flexibility to upgrade to Pro as your deliverability needs grow.

Pro

Get higher deliverability with dedicated infrastructure and proactive reputation protection. Reach the inbox consistently as sending scales.

Pro shifts deliverability from reactive to proactive. With Pro, your sending runs on dedicated IPs, keeping your reputation isolated from other senders. Invalid addresses are caught before they bounce, and you see inbox placement across providers your domains send through, not just within SES. Pro helps you prevent problems, not just discover them after the fact.

Enterprise

Get the most out of Amazon SES. Reach the inbox reliably, stay resilient globally, and isolate sending reputation across workloads.

Everything in Pro, plus resilience that keeps email flowing if a region goes down, reputation isolation across separate workloads, and an annual deliverability assessment. This is the most comprehensive SES experience available.

For full pricing details and a complete feature comparison across all three plans, visit the Amazon SES Pricing page.

Getting started

Starting July 21, 2026, all new SES accounts begin on the Essentials plan. Returning customers who have not sent or processed email through SES since June 1, 2025 also begin on the Essentials plan. From there, you can upgrade to Pro or Enterprise, or switch to à-la-carte pricing at any time. Customers who have sent or processed email through SES on or after June 1, 2025 remain on à-la-carte pricing and can switch to a plan anytime.

If you are new to AWS, the AWS Free Tier provides up to $200 in credits during your first six months that you can apply toward Amazon SES pricing plans. As of July 21, 2026, the SES-specific free tier (3,000 email message charges per month for your first 12 months after first SES use) is no longer available for new customers. If you are currently on the SES-specific free tier, your benefits continue for the remainder of your 12-month period.

To get started, sign in to the Amazon SES console and navigate to the Pricing plan section. To learn more, visit the Amazon SES Pricing page or explore the Amazon SES documentation.


About the Authors

Amazon SQS turns 20: Two decades of reliable messaging at scale

Post Syndicated from Esra Kayabali original https://aws.amazon.com/blogs/aws/amazon-sqs-turns-20-two-decades-of-reliable-messaging-at-scale/

On July 13, 2006, we launched Amazon Simple Queue Service (Amazon SQS) as one of the first three services available to customers, alongside Amazon EC2 and Amazon S3. We had learned firsthand that distributed systems need a reliable way to pass messages between components without creating tight dependencies. If one service called another directly and that service was slow or unavailable, failures cascaded through the entire system. Message queuing solved this by letting services communicate asynchronously: a producer could drop a message into a queue and move on, while a consumer picked it up when ready. This approach kept individual service failures from affecting the rest of the system.

When Amazon SQS launched publicly in July 2006, it made this pattern available to every AWS customer. Twenty years later, that core function, decoupling producers from consumers, remains the reason customers use SQS. The scale, performance, and operational controls around it look very different now though.

Jeff Barr covered the first 15 years of SQS milestones in his 15th anniversary post, from the original 8 KB message limit in 2006 through FIFO queues, server-side encryption, and Lambda integration. Over the last five years, we have continued to scale SQS, added stronger security defaults, and introduced new capabilities that address increasingly complex workload patterns.

Key milestones between 2021 and 2026
High throughput mode for FIFO queues (2021): In May 2021, we launched general availability of high throughput mode for FIFO queues, supporting up to 3,000 transactions per second (TPS) per API action, a tenfold increase over the previous limit. We continued raising this ceiling over the following two years: to 6,000 TPS in October 2022, to 9,000 TPS in August 2023, and to 18,000 TPS in October 2023, before reaching 70,000 TPS per API action in select Regions by November 2023.

Server-side encryption with SSE-SQS (2021): In November 2021, we introduced server-side encryption with Amazon SQS-managed encryption keys (SSE-SQS), giving customers an encryption option that required no key management. In October 2022, we made SSE-SQS the default for all newly created queues, so customers no longer needed to explicitly enable it.

Dead-letter queue redrive enhancements (2021): We progressively expanded how customers recover unconsumed messages from dead-letter queues. In December 2021, we added DLQ redrive to source queue directly in the SQS console. In June 2023, we extended this capability to the AWS SDK and CLI through new APIs, including StartMessageMoveTaskCancelMessageMoveTask, and ListMessageMoveTasks. In November 2023, we added redrive support for FIFO queues.

Attribute-based access control, ABAC (2022): In November 2022, we introduced ABAC, giving customers the ability to configure access permissions based on queue tags rather than maintaining static policies as resources scaled.

JSON protocol support (2023): In November 2023, we added support for the JSON protocol in the AWS SDK, reducing end-to-end message processing latency by up to 23% for a 5 KB payload and lowering client-side CPU and memory usage.

Amazon EventBridge Pipes console integration (2023): We added the ability to connect a queue directly to EventBridge Pipes from the SQS console, routing messages to a broad range of AWS service targets without writing custom integration code.

Extended Client Library for Python (2024): We brought the Extended Client Library, previously available for Java, to Python developers, allowing messages up to 2 GB to be sent through SQS by storing the payload in Amazon S3 and passing a reference through the queue.

FIFO in-flight message limit increase (2024): We increased the in-flight message limit for FIFO queues from 20,000 to 120,000 messages, so consumers can process significantly more messages concurrently without being constrained by the previous ceiling.

Fair queues for multi-tenant workloads (2025): We introduced fair queues to mitigate the noisy neighbor problem in multi-tenant standard queues. By including a message group ID when sending messages, customers can prevent a single tenant from delaying message delivery for others, without any changes required on the consumer side.

1 MiB maximum message payload size (2025): We increased the maximum message payload from 256 KiB to 1 MiB for both standard and FIFO queues, helping customers send larger messages without offloading data to external storage. AWS Lambda event source mapping for SQS was updated in parallel to support the new payload size.

The constant underneath the change
Despite two decades of feature additions, the fundamental use case for SQS has not shifted. Customers use it to decouple services, buffer bursts of traffic, and build systems that stay resilient when individual components fail. That same pattern now extends to AI workloads. Customers use SQS queues to buffer requests to large language models, manage inference throughput, and coordinate communication between autonomous AI agents operating as independent services. For an example of this architecture in practice, read Creating asynchronous AI agents with Amazon Bedrock.

To learn more about Amazon SQS, visit the Amazon SQS product page, review the developer guide, or explore recent updates on the AWS Blogs.

— Esra

Build an AI campaign orchestrator with Amazon Bedrock and AWS End User Messaging

Post Syndicated from Ruchikka Chaudhary original https://aws.amazon.com/blogs/messaging-and-targeting/build-an-ai-campaign-orchestrator-with-amazon-bedrock-and-aws-end-user-messaging/

Marketing teams running large-scale campaigns often send the same message across SMS, WhatsApp, and email regardless of how each customer engages or how many messages they’ve already received that week. This pattern wastes budget on channels customers ignores and pushes promotional content toward frustrated or message-fatigued customers. A MarketingSherpa study found that 45% of consumers who unsubscribe from email marketing cite messages being too frequent as the reason. Over-messaging therefore erodes the audience a brand has paid to acquire. This post shows how to build a campaign orchestrator on AWS End User Messaging and Amazon Bedrock. The orchestrator predicts the best channel for each customer, adapts content per channel, and holds back messages to fatigued or unhappy customers.

In this post, we describe the following capabilities for enterprise marketing teams:

  • Channel prediction that selects SMS, WhatsApp, or email for each customer based on engagement history
  • Content adaptation that takes a single campaign brief and produces channel-appropriate variants: a 160-character SMS, a longer WhatsApp template message, and an HTML email
  • Sentiment-aware suppression that holds back promotional messages when a customer’s stored sentiment score is negative
  • Frequency tracking across channels that lowers send rate when a customer shows disengagement signal
  • Natural-language campaign launch that turns a typed instruction such as “Send the Andaman package to Mumbai customers who haven’t booked in six months” into a segmented, channel-routed send

Prerequisites

You need the following to deploy this solution-

  • An AWS account
  • The AWS Serverless Application Model (AWS SAM) CLI installed locally
  • A WhatsApp Business account linked to AWS End User Messaging Social
  • Amazon Bedrock model access granted for an Anthropic Claude model in your AWS Region
  • (Optional) An Amazon SageMaker AI endpoint for channel prediction. The orchestrator calls the endpoint when it’s configured and falls back to the customer’s stored preferred channel otherwise.

Solution overview

A marketer types a plain-language instruction into the campaign launcher. Amazon API Gateway forwards the instruction to an AWS Lambda function, which starts an AWS Step Functions state machine. The state machine walks the campaign through seven stages. Each stage moves the campaign closer to dispatching the right message on the right channel. The stages read and write customer state in Amazon DynamoDB and call Amazon Bedrock for language tasks. The final stage dispatches messages through AWS End User Messaging or Amazon Simple Email Service (Amazon SES).

When you turn on semantic segmentation, the state machine also queries an Amazon OpenSearch Serverless collection. The collection holds customer embeddings.

To deploy the sample in your account, refer to the GitHub repository.

Figure 1 shows the campaign orchestration system.

Message processing

When a marketer submits an instruction, the launcher Lambda function starts a Step Functions execution. The state machine then runs the stages in order. Each stage reads the output of the previous one, applies its own logic, and passes its result forward. The state machine retries transient failures within a stage, so a Bedrock throttle or a DynamoDB timeout doesn’t restart the whole campaign. A choice state redirects the workflow straight to the recording stage when no customers pass the safety check, so empty campaigns skip the content adaptation step. This decoupled design gives operators three things:

  • If one stage fails, the workflow retries that stage without rerunning earlier work
  • You can add new stages — for example, a translation step — without changing the others
  • The system scales with campaign volume

AI conversation engine

Amazon Bedrock does two distinct things in the orchestrator, and they happen at different stages. The parse stage runs first. It takes the marketer’s plain-language instruction and asks the model to return a small JSON object. The JSON has fields such as location, package, age range, and a short semantic query when the instruction implies a lifestyle or affinity. That JSON is what every downstream stage works against, so the parse output sets the shape of the campaign.The parse stage sends the following prompt to Anthropic Claude on Bedrock through the InvokeModel API:

You parse marketing campaign instructions into structured fields.

Instruction:
{instruction}

Return JSON with these fields:
- "sku" (string or null): product SKU or package name
- "location" (string or null): city or region
- "category" (string or null): one of "Electronics", "Travel", "Apparel", "Home"
- "min_age" (integer or null), "max_age" (integer or null)
- "min_purchases" (integer or null)
- "lookback_days" (integer or null)
- "has_cart_items" (bool or null)
- "semantic_query" (string or null): free-text lifestyle/affinity descriptor

Output ONLY the JSON object, no prose.

For the instruction “Send the Andaman package to budget-conscious families in Mumbai”, the model returns:

{
  "sku": "Andaman package",
  "location": "Mumbai",
  "category": "Travel",
  "min_age": null, "max_age": null,
  "min_purchases": null,
  "lookback_days": null,
  "has_cart_items": null,
  "semantic_query": "budget-conscious families"
}

The adapt stage runs later, after segmentation and safety. It takes a single campaign brief and asks Bedrock to produce one variant per channel: a 160-character SMS, a longer WhatsApp template message, and an HTML email. The model never sees customer-level data at this point; the brief and the channel are the only inputs.The orchestrator stores one prompt per channel. The SMS prompt enforces a hard character limit; the WhatsApp prompt allows a longer message; the email prompt asks for structured HTML:

# SMS
Write a single SMS for the campaign brief below. Hard limit: 160 characters.
No emojis, no links unless the brief explicitly includes one. Plain text only.

# WhatsApp
Write a WhatsApp message for the campaign brief below. Up to 1024 characters.
Friendly tone, optional emoji where natural.

# Email
Write an HTML email body for the campaign brief below. Include a single <h1>,
two short paragraphs, and a call-to-action link placeholder {{CTA_URL}}.
No <html> or <body> wrappers.

Each prompt is formatted with the campaign brief and sent to Bedrock; the response becomes that channel’s variant for every approved customer in the segment.

The safety stage runs after the parse stage and before the adapt stage, and it is rule-based rather than model-based. It reads each customer’s stored sentiment score and rolling send count from DynamoDB, and drops customers below the sentiment threshold (default -0.3) or above the fatigue limit. The fatigue limit is a per-customer count of sends over a rolling window, for example five sends in the previous seven days. You set the fatigue window and the sentiment threshold as Step Functions input parameters. You populate the sentiment score upstream. For example, you can run a daily Amazon Comprehend Custom Classification job that scores recent support transcripts and writes the result back to the customer record.

The fatigue check reads the customer’s recent send timestamps from the rate-limits table and counts the entries inside the rolling window:

NEGATIVE_THRESHOLD     = Decimal("-0.3")     # configurable
MAX_MESSAGES_PER_WINDOW = 5
WINDOW_SECONDS         = 7 * 24 * 60 * 60     # 7 days

def _is_fatigued(customer_id):
    item = _rate_limits.get_item(Key={"limiter_key": f"customer:{customer_id}"}).get("Item")
    if not item:
        return False
    cutoff = int(time.time()) - WINDOW_SECONDS
    recent = [t for t in item.get("recent_sends", []) if int(t) >= cutoff]
    return len(recent) >= MAX_MESSAGES_PER_WINDOW

Each successful send writes its timestamp into the customer’s recent_sends list, so the next campaign sees an up-to-date fatigue count without a separate ETL step.

Orchestration

Each stage in the campaign workflow is a small AWS Lambda function. The state machine invokes them in sequence: parse the instruction, segment customers, predict channels, check safety, adapt content, deliver messages, and record results. The predict stage reads each customer’s per-channel engagement history from DynamoDB and picks the channel with the highest historical engagement rate. When you wire an Amazon SageMaker AI endpoint into the stack, the stage calls that endpoint instead and uses its score as the channel ranking signal.The state machine, not the functions, owns the control flow. New stages (for example, a translation step before adapt content) can be inserted without changing the existing handlers. The Step Functions definition lives in statemachine/campaign_orchestrator.asl.json. Refer to it in the GitHub repository for the exact state graph and retry policy.

Semantic search

Consider a marketer who types “Send the Andaman package to budget-conscious families interested in beach vacations.” A keyword filter against the customer table won’t match a profile tagged “economy package, kid-friendly, coastal”, because the words don’t overlap even though the meaning does. To bridge that gap, the seed script embeds each customer profile with Amazon Titan Text Embeddings v2 and writes the vector into an OpenSearch Serverless Vector search collection. The segment stage then embeds the marketer’s phrasing at query time and runs a k-nearest-neighbor search against the collection.

The orchestrator intersects those matches with the structured DynamoDB filter. The final segment respects both the hard constraints (location, age, recency) and the soft ones (lifestyle, affinity). OpenSearch Serverless scales the collection’s compute units to zero when idle, so this capability adds near-zero cost when no campaigns run.

Deployment

To deploy the sample in your AWS account, clone the GitHub repository and run the SAM-based deploy script:

git clone https://github.com/aws-samples/sample-ai-campaign-orchestrator.git
cd sample-ai-campaign-orchestrator
./scripts/deploy.sh --guided

The script prompts you for an AWS Region, a stack name, and the orchestrator parameters (your WhatsApp phone number ID and optional SES sender). It then runs sam build followed by sam deploy, and prints the API endpoint and stack outputs when the deployment finishes.

Test the solution

After the stack finishes deploying, seed the customer profiles table with one sample customer and run a campaign against it:

python scripts/seed_demo_data.py --whatsapp-recipient +1234567890

Then submit a campaign instruction to the API endpoint that the deploy script printed:

curl -X POST $ENDPOINT -H 'content-type: application/json' \
  -d '{"instruction": "Send the Andaman package to Mumbai customers"}'

From here you can:

  1. Watch the campaign execution in the AWS Step Functions console.
  2. Query the delivery tracking table in Amazon DynamoDB to see which customers the safety stage approved or suppressed, and which channel the orchestrator picked for each.
  3. Check the recipient’s phone for the WhatsApp template message that the deliver stage sent.

Sample conversation

The recording in this section shows a marketer using the campaign launcher to send an Andaman travel promotion to a Mumbai segment. It opens with the marketer typing the natural-language instruction and the parse stage extracting structured filters. The segment stage then matches customers in DynamoDB. The safety stage suppresses a customer with a low sentiment score. The predict stage assigns a channel per remaining customer. The recording ends with the adapt stage producing one message variant per channel and the deliver stage dispatching them through AWS End User Messaging.

Clean up

To avoid incurring future charges, delete the resources you created. The sample includes a cleanup script in the GitHub repository. Run ./scripts/cleanup.sh to empty the deployment bucket and delete the stack. The stack deletion removes the AWS Step Functions state machine, AWS Lambda functions, Amazon DynamoDB tables, and (when configured) the Amazon OpenSearch Serverless collection.

Conclusion

You can combine AWS End User Messaging, Amazon Bedrock, and AWS Step Functions to build a campaign orchestrator. The orchestrator routes each message to the channel a customer is most likely to open. It also holds back sends to fatigued or unhappy customers.

The same pattern fits other business-initiated messaging workflows where per-recipient channel and content decisions matter. Examples include transactional banking notifications, appointment reminders, and logistics status updates. To deploy the sample in your account, refer to the GitHub repository. To learn more about AWS End User Messaging, refer to the service documentation.

If you’re applying this pattern, start with the safety check and frequency tracking. Those two stages reduce the risk of damaging customer relationships and produce the engagement data that channel prediction depends on. Once that data is in place, add the prediction and content adaptation stages. Use this implementation as a reference for production messaging on AWS.


About the authors

Build an autonomous ecommerce assistant with AWS End User Messaging, Amazon Bedrock AgentCore, and OpenClaw

Post Syndicated from Rommel Sunga original https://aws.amazon.com/blogs/messaging-and-targeting/build-an-autonomous-ecommerce-assistant-with-aws-end-user-messaging-amazon-bedrock-agentcore-and-openclaw/

In this post, we walk through Claw Boutique, an open-source reference architecture that connects a web storefront, WhatsApp, email, and Telegram into a single OpenClaw-driven ecommerce experience on AWS. Buyers interact through WhatsApp and a web store. The shop owner manages everything from Telegram, where an artificial intelligence (AI) agent processes restock, refund, and order commands.

The project uses Amazon Bedrock AgentCore Runtime with the Strands Agents SDK, an open-source framework for building AI agents, for real-time buyer chat. Amazon Elastic Kubernetes Service (Amazon EKS) hosts the seller-side AI agent (OpenClaw, an open-source AI agent gateway). AWS End User Messaging Social provides managed WhatsApp Business integration. The entire stack deploys with a single AWS Cloud Development Kit (AWS CDK) command.

Architecture overview

The architecture separates concerns into three channels that share a common Store API and database.

Claw Boutique architecture on AWS showing the buyer, seller, and web storefront channels

Figure 1 – Claw Boutique architecture on AWS

Buyer channel (WhatsApp): Inbound WhatsApp messages arrive through AWS End User Messaging Social, which provides a managed WhatsApp Business API integration. Messages publish to an Amazon Simple Notification Service (Amazon SNS) topic, which triggers a Dispatcher AWS Lambda function. The dispatcher invokes a Strands Agent hosted on Amazon Bedrock AgentCore Runtime, running Amazon Nova Lite for real-time, tool-calling conversations. AgentCore Memory provides session continuity across messages. The agent can look up products, check order status, escalate issues, and send replies back through WhatsApp.

Seller channel (Telegram): The store owner receives stock alerts, review escalations, and order notifications on Telegram. An AI agent runs on Amazon EKS via the OpenClaw gateway. The owner replies with natural language commands such as “restock hoodies” or “apologize to the buyer,” and the agent runs the appropriate Store API calls.

Web storefront: Amazon CloudFront serves a static site from Amazon Simple Storage Service (Amazon S3). The checkout flow calls the Store API through Amazon API Gateway. The same API backs both the storefront and the admin dashboard.

All three channels converge on a single Store API Lambda function (Python/Flask) backed by Amazon Relational Database Service (Amazon RDS) for MySQL. Amazon Simple Email Service (Amazon SES) sends transactional email messages for order confirmations, shipping updates, and refund notices.

How it works: The order lifecycle

A single order touches the web storefront, WhatsApp, email, Telegram, and the admin dashboard. Here is the full flow.

1. Place an order

You visit the storefront, add items to the cart, and check out. The Store API creates the order in Amazon RDS and returns an order number.

The Claw Boutique web storefront with product listings

Figure 2 – The Claw Boutique storefront

2. Order confirmation on WhatsApp and email

Two things happen right after checkout. The buyer receives a WhatsApp message with the order number, items, and total, followed by a feedback survey asking them to rate their experience from 1 to 5. At the same time, Amazon SES sends a confirmation email with the same order details.

WhatsApp order confirmation message followed by a feedback survey rating prompt

Figure 3 – WhatsApp order confirmation and feedback survey

Order confirmation email sent through Amazon SES

Figure 4 – Order confirmation email via Amazon SES

3. Stock alert on Telegram

Every purchase triggers a stock check. If any item is out of stock, running low (fewer than 5 units), or projected to sell out within 7 days, the seller gets a Telegram alert with current stock levels and sell-through rates. The seller can reply with a command such as “restock hoodies 20” and the AI agent runs it.

Telegram stock alert showing stock levels with a restock command reply

Figure 5 – Telegram stock alert with restock command

4. Negative feedback triggers an escalation

The buyer replies “1” to the WhatsApp survey. The Store API creates an escalation record and sends the seller a Telegram alert with the buyer’s name, phone number, rating, and review text.

Telegram review escalation alert with buyer details and rating

Figure 6 – Telegram review escalation alert

5. Seller resolves the issue from Telegram

The seller replies “apologize” on Telegram. The AI agent looks up the unresolved escalation and takes four actions: sends a WhatsApp apology to the buyer, sends a refund confirmation email via Amazon SES, marks the order as “refunded” in the database, and resolves the escalation. If there are multiple open escalations, the agent lists them and asks which one to resolve.

6. Admin dashboard

The seller can also open the admin dashboard to view orders (now showing “refunded” status), escalation history, stock levels, and AI-generated business insights based on order patterns and buyer feedback.

Admin dashboard showing orders, escalation history, stock levels, and business insights

Figure 7 – Admin dashboard with orders and insights

Ordering directly through WhatsApp

Buyers can also browse and order by texting the WhatsApp business number directly. The Strands Agent on AgentCore manages the full conversation: showing available products, checking order status, answering product questions, and escalating issues to the store owner.

Ordering through WhatsApp using Amazon Bedrock AgentCore

Figure 8 – Ordering through WhatsApp via Amazon Bedrock AgentCore

Why two AI models?

Claw Boutique uses two AI models for different purposes, each chosen for the characteristics that matter most in its channel.

Amazon Nova Lite (via Amazon Bedrock AgentCore) for the buyer channel: Buyer-facing WhatsApp interactions need to be fast and cost-effective. Amazon Nova Lite provides sub-second responses with reliable tool calling at a fraction of the cost of larger models. AgentCore Runtime hosts the agent container, while AgentCore Memory manages conversation history per buyer phone number. The Strands Agents SDK handles tool definitions, orchestration, and model interaction with minimal boilerplate.

AI agent (via OpenClaw on Amazon EKS) for the seller channel: The seller channel involves more complex tasks: interpreting ambiguous commands, managing multi-step workflows (such as resolving escalations that span WhatsApp, email, and the database), and generating business insights. The model’s reasoning capabilities are well suited for these. OpenClaw provides the gateway, tool execution, and memory management layer.

This approach keeps buyer-facing latency low and costs predictable, while giving the seller access to deeper reasoning when managing the business.

Prerequisites

Before you deploy, make sure you have the following:

  • AWS Command Line Interface (AWS CLI) configured with credentials.
  • Node.js 18+ and Docker running locally.
  • A Telegram bot token (obtainable from @BotFather).
  • A WhatsApp Business Account linked to AWS End User Messaging Social.
  • A verified Amazon SES email address.

Deploying the solution

The entire stack deploys with AWS CDK. A single cdk deploy command provisions the Amazon Virtual Private Cloud (Amazon VPC), Amazon EKS cluster, Amazon RDS database, Lambda functions, Amazon API Gateway, Amazon CloudFront distribution, Amazon S3 bucket, Amazon SNS topic, and all AWS Identity and Access Management (IAM) roles and security groups. AWS CDK also runs database initialization (schema and seed data), Docker image build, Amazon Elastic Container Registry (Amazon ECR) push, and Amazon EKS deployment.

Configuration values (Telegram token, WhatsApp IDs, Amazon SES email) go into a CDK context file. Cold deploy takes about 25-30 minutes.

You can find the full source code and deployment instructions in the GitHub repository.

Cleaning up

To avoid ongoing charges, delete the resources created in this walkthrough when you’re done experimenting. Run the following command from the cdk/ directory:

cd cdk && npx cdk destroy

This removes the Amazon EKS cluster, Amazon RDS database, Lambda functions, and all other resources created by the stack. No context values are needed for destroy.

Conclusion

In this post, we showed how to build an ecommerce bot using OpenClaw and Amazon Bedrock AgentCore. By combining AWS End User Messaging Social for WhatsApp, Amazon Bedrock AgentCore Runtime for real-time buyer conversations, and Amazon EKS for a seller-side AI agent, you can create a system where buyers order through the channels they already use, and store owners manage their business from a single Telegram chat.

The project is open source and deploys with a single AWS CDK command. You can use it as a starting point and adapt it to your own product catalog, messaging channels, and business logic.

To learn more and get started:


About the authors

Global SMS strategy for gaming: Lessons from migrating 2FA across 100+ countries

Post Syndicated from Jose Velazquez Hernandez original https://aws.amazon.com/blogs/messaging-and-targeting/global-sms-strategy-for-gaming-lessons-from-migrating-2fa-across-100-countries/

When a player in Turkey, Brazil, or India tries to log in, they expect a verification code on their phone within seconds. If it doesn’t arrive, that’s a lost session, a support ticket, or worse, a player who doesn’t come back. For gaming studios operating across 100+ countries, delivering those SMS messages reliably is a complex operational challenge involving country-specific regulations, carrier registrations, and routing rules that can change overnight.

This post shares practical lessons from a real-world migration of a global gaming SMS platform from Amazon Simple Notification Service (Amazon SNS) to AWS End User Messaging. You’ll learn how to use resource sharing to avoid months of re-registration, how to build a country inventory that survives regulatory changes, and how to avoid the operational pitfalls that surface only when you migrate production traffic across accounts. Although the examples here are drawn from gaming, the operational challenges are universal. Any organization delivering SMS at international scale will face the same complexities, whether your users are players, customers, or patients.

Why gaming studios migrate SMS accounts

Before diving into the how, it’s worth understanding the why. Gaming studios commonly need to move SMS workloads between AWS accounts for several reasons:

  • Security and scope isolation — Separating messaging infrastructure from game servers and backend services into a dedicated account reduces the impact of a security incident in either environment.
  • Organizational changes — Studio acquisitions, team restructuring, or moving from a shared corporate account to a studio-owned account.
  • Service evolution — Migrating from Amazon SNS (which supports SMS as one of many pub/sub capabilities) to AWS End User Messaging (purpose-built for SMS/MMS/voice with features like phone poolsprotect configurations, and event destinations)
  • Compliance boundaries — Isolating messaging resources to meet data residency or audit requirements.

Whatever the reason, the challenge is the same: you have a working global SMS setup with phone numbers, Sender IDs, short codes, and carrier registrations spread across dozens of countries, and you need to move it without disrupting millions of players’ 2FA flows.

Resource sharing: A faster migration path

The traditional approach would be to re-register every origination identity (that is, phone numbers, Sender IDs, short codes, see Choosing an origination identity) in the consuming account from scratch. For a global gaming platform, this is impractical:

Multiply these timelines across every country where you operate, and a “clean” re-registration migration could take months.

AWS Resource Access Manager (RAM) offers a better path. Instead of re-registering, you share your existing origination identities from the sharing account to the consuming account, which can immediately begin sending. For the step-by-step setup including CLI commands, AWS Identity and Access Management (IAM) policies, and architecture diagrams, see A Complete Guide to Resource Sharing for AWS End User Messaging. This post focuses on the operational challenges that surface at global scale.

Know your country landscape before you start

The single most important preparation step is building a complete inventory of your SMS footprint, country by country. AWS publishes a comprehensive reference table listing supported countries, origination identity types, and registration requirements.

For a ready-made planning template, download the Global SMS Planning Sheet from How to Manage Global Sending of SMS with AWS End User Messaging. Your inventory should go beyond what’s published: document exact registered values (case matters), fallback behavior, carrier/aggregator, and why you chose each approach.

This inventory becomes invaluable for future account transitions, incident response, and onboarding new team members.

Plan for regulatory change, not just today’s rules

The global SMS regulatory landscape is not static. It’s tightening. Countries that previously had relaxed requirements are increasingly introducing mandatory Sender ID registration, content filtering, and message format restrictions. A migration is the ideal time to future-proof your setup, not just replicate what worked yesterday.

For help navigating the registration process for specific countries, see Registration support.

Example: Turkey’s URL blocking regulation (April 2026)

In April 2026, Turkey’s Information and Communications Technologies Authority (BTK) began enforcing a regulation that blocks all international A2P SMS messages containing URLs, hyperlinks, or shortened links. For gaming studios that include account recovery links, promotional URLs, or support links in their SMS messages, this type of regulatory change can mean immediate delivery failure to an entire market.

This type of regulatory shift is becoming more common:

  • Optional to mandatory Sender ID registration. Australia is mandating Sender ID registration with ACMA by mid-2026. AWS now supports Australia sender ID registration directly through the EUM console.
  • Content template registration. India requires DLT registration through TRAI, and China requires message template pre-registration through AWS Support.
  • Content filtering and URL restrictions. Carriers in multiple markets are implementing automated filtering that rejects messages containing URLs or specific patterns, even from registered senders. Your country inventory shouldn’t just document what works today. It should also track regulatory change dates, upcoming requirements, content restrictions, and your fallback plan for each market. For each country, ask: “If the rules change tomorrow, how quickly can we adapt?” If the answer is weeks, that country should have a fallback channel (email, push notification) ready to activate immediately.

Diagram showing the global SMS migration workflow from a sharing account to a consuming account using AWS RAM

Lessons learned: What can go wrong at scale

Even with careful planning, migrating global SMS infrastructure surfaces unexpected issues. The following sections cover the most common pitfalls we encountered.

Sender ID case sensitivity

As documented by AWS, Sender ID values are case-sensitive at the carrier and aggregator level. In countries with mandatory registration (Turkey, UK, India, Australia), carriers might perform exact-match validation. If your application sends STUDIONAME but the carrier has StudioName on file, the message can be rejected.

The AWS End User Messaging console displays all Sender IDs in uppercase regardless of registration casing, which is something to watch out for. Always verify casing against the Registrations section of the console, which preserves the original value. Countries can also begin enforcing stricter matching overnight as regulations tighten, turning previously-working messages into failures.

What to watch for:

  • Test delivery to countries with mandatory Sender ID registration before rolling out any casing changes globally.
  • Check the country capabilities table for countries marked “Registration required.”

Phone pool quotas for global operations

Phone pools are the recommended way to manage origination identities (that is, phone numbers, Sender IDs, short codes, see Choosing an origination identity) at scale with AWS End User Messaging. A pool automatically selects the appropriate identity based on the destination country code, handling failover between identities for the same country.

You can find the default quota for origination identities per phone pool in Quotas for AWS End User Messaging SMS. You can request increases through the Service Quotas console, or contact AWS Support for larger increases.

Plan ahead: Inventory your origination identities before creating your pool, and request the quota increase before you start associating resources. Hitting the quota mid-migration creates unnecessary delays.

Batch your RAM sharing operations

When sharing a large number of origination identities through RAM (50+), the RAM API creates resource-based policies on each shared resource. The order of operations matters: add the consuming account (principal) to the resource share first, then add resources incrementally. If you create a resource share with hundreds of resources and then add the consuming account afterward, RAM attempts to set resource-based policies on all resources simultaneously. This is the most common trigger for throttling at scale. In severe cases, this throttling can result in resources with empty or corrupted resource policies, making them inaccessible to the consuming account until manually remediated.

Best practice:

  • Add the consuming account (principal) to the resource share before adding any resources. Batch resource associations in groups of 10 or fewer.
  • After each batch, verify that all resources have reached ASSOCIATED status.
  • From the consuming account, confirm that shared resources are visible using the describe-* CLI commands with the --owner SHARED flag.
  • Retain AWS CloudTrail logs for troubleshooting if any associations don’t complete as expected.
  • If you suspect corrupted policies, verify the resource policies on affected resources and re-associate them individually.

Not everything transfers through RAM

RAM sharing covers the “what you send with”: phone numbers, Sender IDs, pools, and opt-out lists. But several critical components are account-specific and must be recreated in the consuming account:

Note on Protect Configurations: Protect Configurations apply at three levels: account default, configuration set, and per-message. A common mistake when setting up a consuming account is checking only the account-level default and missing a restriction at the configuration set level, resulting in unexpectedly blocked destination countries. When recreating your configuration in the consuming account, verify protect settings at all three levels.

Production mode requirement: Both the sharing account and the consuming account must be in Production mode. If the consuming account is still in the SMS sandbox, it can only send messages to verified destination phone numbers, which means shared production-ready resources cannot be used for real-world traffic. Verify production status in the consuming account before beginning your migration.

One tip worth highlighting: The registration process for certain countries’ message templates (notably China) is handled through AWS Support cases. After the registration is approved, retain a copy of the approved template details, the case correspondence, and the registration parameters. Support case history might not be accessible indefinitely. If you need to re-register in a new account later, having your own records of what was approved and the exact template content saves significant time.

Countries without Sender ID support

Not every country supports Sender IDs. If your phone pool is configured with Sender IDs and a message is routed to a country that doesn’t support them (for example, Belgium or Puerto Rico), the delivery will fail, and the error might not clearly indicate that the issue is Sender ID incompatibility.

Build an exclusion list: For countries that don’t support Sender IDs you have two options:

  • Route those messages through a different origination identity (long code or short code) if available.
  • Exclude those countries from Sender ID routing and allow them to fall back to shared routes.

The country capabilities table is your definitive reference for which countries support which origination identity types.

The “hidden fallback” discovery

A common surprise during migration is that your existing setup might have been silently falling back to shared routes in countries where you assumed you had branded Sender ID delivery. When SMS is sent through Amazon SNS with automatic origination identity selection, the service quietly falls back to shared routes (often displaying “NOTICE” as the Sender ID) if no registered identity is available for the destination country.

This fallback is invisible in normal operations. Messages still deliver, and unless you’re checking the Sender ID displayed on the recipient’s device, there’s no visible indication. But when you migrate to a phone pool with explicit Sender ID routing, these gaps become visible as delivery failures.

Before migrating, audit your actual delivery patterns, not just your configuration. Check delivery reports for countries where you expect branded Sender ID delivery and verify that the Sender ID displayed matches your brand, not “NOTICE” or a generic shared route.

Operational recommendations

The following practices help you avoid disruptions during migration and maintain reliable delivery afterward.

Have a fallback channel ready during migration

Before you begin migrating traffic, ensure you have an alternative authentication channel (typically email-based 2FA) ready to activate on short notice per-country. When an issue is detected, disable SMS for that country and fall back to email while you investigate, rather than leaving players locked out.

Build your country registry

Maintain a living document that tracks what you have (identity type, registration status, exact casing, account location), what you chose and why, what gaps exist (countries relying on shared routes), and registration details (approval dates, case references, template content). This registry is your migration playbook.

Set up delivery monitoring from day one

In the consuming account, configure Configuration Sets with Event Destinations before sending your first message. Consider using the Message Feedback API to track actual OTP conversion, not just carrier delivery. See Track OTP Success with AWS End User Messaging SMS Feedback for implementation details.

Test country by country, not just “it works”

Before scaling traffic, test delivery to representative countries from each category: mandatory Sender ID registration, no Sender ID support, optional Sender IDs, message template registration, and carrier-specific content restrictions. A successful test to the US doesn’t validate your Turkey configuration.

Conclusion

Global SMS for gaming works reliably until something needs to change. Migrations, account restructuring, and service upgrades expose the complexity that was previously hidden behind automatic fallbacks and “it just works” behavior.

In this post, we covered how to use RAM sharing to avoid months of re-registration, how to build a country inventory that accounts for regulatory change, and how to navigate the operational pitfalls of migrating production SMS traffic across accounts. The key takeaways:

  1. Use RAM sharing to avoid months of re-registration. See A Complete Guide to Resource Sharing for AWS End User Messaging for the step-by-step setup.
  2. Know your country landscape before you start. The country capabilities table is your starting point, but build your own registry with registration details, exact Sender ID values, and decision rationale.
  3. Respect the details. Case sensitivity, phone pool quotas, RAM batching limits, non-shareable resources, and record retention can each independently block your migration.
  4. Monitor from day one and plan for regulatory change. Set up delivery tracking before sending production traffic, and track what’s changing in each market.

What matters to players is that their verification code arrives in seconds, every time, regardless of location. The operational discipline described in this post helps make that happen and keeps it working through whatever infrastructure changes come next. Although this post is framed around gaming, the lessons apply broadly. Whether your users are players, shoppers, or account holders, reliable global SMS delivery requires the same operational rigor.

References

About the author

Getting your SMS short code production-ready with AWS End User Messaging

Post Syndicated from Harshvardhan Chunawala original https://aws.amazon.com/blogs/messaging-and-targeting/getting-your-sms-short-code-production-ready-with-aws-end-user-messaging/

Getting your Short Message Service (SMS) short code production-ready requires you to configure the Amazon Web Services (AWS) infrastructure that controls how your messages are sent, monitored, and protected. You have provisioned your short code, and it is active on carrier networks. In this post, we walk through that setup using AWS End User Messaging SMS, covering 12 configuration steps from compliance through phased traffic migration. Total estimated time is 2 to 4 hours of configuration plus 1 to 3 business days for limit increase approvals.mess

The guide to SMS short codes with AWS End User Messaging covers the application and registration process up through provisioning. This post picks up from that point and provides an operational readiness walkthrough that takes you from “Active” status to confidently sending your first production message, including a final validation step to confirm readiness.

The following diagram shows the end-to-end message flow and event routing architecture covered in this walkthrough.

End-to-end SMS short code architecture showing message flow from sender through AWS End User Messaging SMS to carriers and recipient handsets, with event routing to Amazon CloudWatch, Amazon Simple Notification Service (Amazon SNS), and Amazon Data Firehose destinations

Prerequisites

You need the following to follow along with this walkthrough:

  1. An AWS account with access to the AWS End User Messaging SMS console.
  2. A short code with Active status in the AWS Management Console (carrier provisioning finished).
  3. Permissions to create AWS Identity and Access Management (IAM) roles, Amazon CloudWatch Log Groups, and Amazon Simple Notification Service (Amazon SNS) topics.
  4. AWS Command Line Interface (AWS CLI) v2 or an AWS SDK installed and configured.
  5. Your approved registration documentation, including the service name, keyword responses, and message templates submitted to carriers.

Step 1: Verify your short code is active and delivering

Navigate to the AWS End User Messaging SMS console, choose Phone numbers, and locate your provisioned short code. Confirm that the status shows Active, then send a test message to a phone number you control using the SendTextMessage API or the console test feature. Verify delivery on your handset.

Carrier-side activation can take up to 24 to 48 hours to fully propagate across all networks after provisioning finishes. If the console shows Active but your test message does not arrive, submit a support case so the team can verify propagation status with the carrier.

You can also verify using the AWS CLI:

aws pinpoint-sms-voice-v2 send-text-message \
    --destination-phone-number "+15555550100" \
    --origination-identity "12345" \
    --message-body "Test message from short code" \
    --message-type TRANSACTIONAL \
    --configuration-set-name "prod-otp-shortcode"
# Replace +15555550100 with your test phone number, 12345 with your short
# code, and prod-otp-shortcode with your configuration set name from Step 3.

Step 2: Configure keywords and verify message compliance

US carriers require every short code to respond to HELP and STOP keywords. You defined these during your registration, and this step confirms they are configured correctly in your account.

In the SMS console, choose Phone numbers, select your short code, and choose the Keywords tab. Verify that STOP returns the opt-out response you submitted during registration, and that HELP returns your support contact response (which must include a phone number or email). Add any custom keywords your use case requires, such as YES for double opt-in confirmation flows. You can manage keywords programmatically using the PutKeyword API.

To add or update a keyword programmatically:

aws pinpoint-sms-voice-v2 put-keyword \
    --origination-identity "12345" \
    --keyword "YES" \
    --keyword-message "You have confirmed your subscription to Acme Health Alerts. Msg&data rates may apply. Reply STOP to opt out." \
    --keyword-action AUTOMATIC_RESPONSE
# Replace 12345 with your short code, YES with your custom keyword, and the
# keyword-message text with your approved response.

To verify your current keyword configuration:

aws pinpoint-sms-voice-v2 describe-keywords \
    --origination-identity "12345"
# Replace 12345 with your short code.

Beyond keyword configuration, carrier compliance does not end at registration approval. The content you send in production must stay aligned with what carriers reviewed and approved. Here is what to keep consistent.

Use the exact brand or program name from your approved registration across all keyword responses, confirmation messages, and outbound templates. If carriers approved your registration under “Acme Health Alerts,” every message your short code sends should reference that name. Mixing variations creates inconsistencies that auditors flag during reviews. For example, do not use the company name in one message and the product name in another.

Your HELP, STOP, and confirmation responses must match the templates submitted during registration. Do not add or remove opt-out language, change frequency disclosures, or alter customer care contact details post-approval without updating the registration through a support case. If your organization operates multiple domains, use the domain documented in the registration. For example, you might have one domain for the application and another for marketing. Carrier reviewers cross-reference message content, opt-in screenshots, and privacy policy URLs with what was submitted.

Humans conduct carrier reviews, and message content that is concise and limited to the essentials is reviewed consistently. All messages must remain under 160 characters.

Step 3: Create a configuration set with event destinations

A configuration set controls where your SMS delivery events are streamed and which event types are captured. Without one, you are limited to the basic events that AWS End User Messaging SMS sends to Amazon EventBridge by default. These default events omit recipient details and full carrier response context.

Create a configuration set with a descriptive name such as prod-otp-shortcode or marketing-sc-us. Then create at least one event destination. The three main options are Amazon CloudWatch Logs (for operational monitoring and alarming), Amazon SNS (for real-time event fanout to downstream systems), and Amazon Data Firehose (for durable archival and analytics).

Amazon Data Firehose typically delivers to an Amazon Simple Storage Service (Amazon S3) bucket, where you can query delivery history using Amazon Athena for compliance audits or delivery pattern analysis.

# Create the configuration set
aws pinpoint-sms-voice-v2 create-configuration-set \
    --configuration-set-name "prod-otp-shortcode"

# Add a CloudWatch Logs event destination
aws pinpoint-sms-voice-v2 create-event-destination \
    --configuration-set-name "prod-otp-shortcode" \
    --event-destination-name "otp-delivery-logs" \
    --matching-event-types TEXT_DELIVERED TEXT_FAILED TEXT_QUEUED TEXT_CARRIER_UNREACHABLE TEXT_TTL_EXPIRED \
    --cloud-watch-logs-destination '{
        "IamRoleArn": "arn:aws:iam::123456789012:role/SMSEventsToCloudWatch",
        "LogGroupArn": "arn:aws:logs:us-east-1:123456789012:log-group:/aws/sms/prod-otp-shortcode"
    }'
# Replace prod-otp-shortcode with your configuration set name, otp-delivery-logs
# with a descriptive destination name, and the ARN values with your IAM role ARN
# (must have logs:PutLogEvents permission) and CloudWatch Log Group ARN.

Important: When sending messages with SendTextMessage, always specify your ConfigurationSetName parameter so events route to the appropriate destination.

Required event types

Event type Description
TEXT_DELIVERED Message successfully delivered to recipient handset.
TEXT_FAILED Message delivery failed.
TEXT_QUEUED Message accepted and queued for delivery.
TEXT_CARRIER_UNREACHABLE Carrier network unreachable.
TEXT_TTL_EXPIRED Message expired before delivery.

For a detailed walkthrough of configuration sets including multi-tenant architectures, see How to send SMS using configuration sets with AWS End User Messaging.

Step 4: Create a phone pool and associate your short code

A pool is a logical container that groups origination identities and controls routing behavior. Creating one gives you deterministic control over which number sends your messages and how opt-outs are enforced.

# Create the pool
aws pinpoint-sms-voice-v2 create-pool \
    --origination-identity "12345" \
    --iso-country-code "US" \
    --message-type TRANSACTIONAL

# Disable shared routes so only your short code is used
aws pinpoint-sms-voice-v2 update-pool \
    --pool-id "pool-1234567890abcdef0" \
    --shared-routes-enabled false
# Replace 12345 with your short code, US with your destination country code,
# and pool-1234567890abcdef0 with the Pool ID returned by create-pool.

Configuration parameters

Parameter Recommended value Rationale
Pool name us-otp-pool Descriptive, environment-prefixed.
SharedRoutesEnabled False Prevents fallback to shared routes; only your short code is used.
Opt-out list Associate one Manages opt-out state per use case.
IsoCountryCode US Restricts to destination country your short code serves.

If you operate multiple use cases on separate short codes, create a dedicated pool for each. For example, use one short code for one-time password (OTP) traffic and another for transactional notifications. This isolation means a recipient opting out of marketing messages does not lose access to authentication codes.

Step 5: Request your throughput increase

Short codes start at a default of 100 messages per second (MPS). If your production volume will exceed this, request an increase before your launch date rather than after traffic is flowing.

Create a case in the AWS Support Center, choose Service limit increase, then choose End User Messaging SMS. Provide your short code phone number, requested MPS, use case description, and expected peak volume. Allow 1 to 3 business days for processing.

To estimate your required MPS:

Required MPS = (Peak hourly volume / 3,600) x 2

Short codes support scaling to thousands of MPS, so start with a value that covers your expected peak and request further increases as traffic grows.

Step 6: Request a spending limit increase

AWS accounts have a default monthly SMS spending limit. To keep delivery uninterrupted at your expected volume, request an increase that accommodates your projected monthly spend before you begin sending.

Create a support case under Service limit increase > End User Messaging SMS > Account Spend Threshold. Provide your estimated monthly spend, use case description, and website URL.

For details, see Requesting increases to your monthly SMS spending quota.

Step 7: Restrict destination countries

If your short code serves a single country (US-only, for example), restrict sending to that country. This protects your account from artificially inflated traffic (SMS pumping). In pumping attacks, messages are routed to international premium-rate numbers, generating significant charges.

In the SMS console, navigate to Account settings, then choose Countries and keep only the countries you intend to send to. The pool-level IsoCountryCode restriction from Step 4 provides an additional enforcement layer at the sending path. Combining account-level country restrictions with pool-level country codes gives you two independent controls that both must be satisfied before a message is sent internationally.

For a detailed walkthrough on SMS fraud prevention controls, see Defending against SMS pumping: new AWS features to help combat artificially inflated traffic.

Step 8: Set up monitoring and alarms

With event destinations configured in Step 3, build proactive alerting that surfaces delivery trends before they affect your end users.

Alarm Metric / Source Threshold
Delivery success rate CloudWatch SMS metrics Alert when below 95%.
Spend threshold CloudWatch billing metric Alert at 80% of monthly limit.
Delivery failures Amazon EventBridge rule on TEXT_FAILED Route to Amazon SNS topic or AWS Lambda.
Carrier unreachable Amazon EventBridge rule on TEXT_CARRIER_UNREACHABLE Route to Amazon SNS topic or AWS Lambda.

Build a CloudWatch dashboard showing messages sent per minute, success versus failure breakdown, and spend accumulation over time.

You can also configure Amazon EventBridge to notify you of registration status changes. AWS End User Messaging SMS publishes events for statuses including REQUIRES_UPDATES, REVIEWING, and PROVISIONING, which is useful if a carrier requests changes during a proactive audit after your short code is already active.

For metric details, see Monitoring SMS activity with Amazon CloudWatch.

Step 9: Track OTP verification success (if applicable)

If your short code delivers OTP or two-factor authentication (2FA) codes, track end-to-end verification success in addition to carrier delivery receipts. A “delivered” status at the carrier level does not confirm the end user received and entered the code.

Tracking verification rates gives you insight into latency patterns when codes expire before arrival, geographic delivery trends, and opportunities to improve conversion. Some use cases involve asynchronous processing where several minutes of computation occur before the SMS is sent. For these, measure the full round-trip from the triggering action to message delivery. This separates application-side latency from carrier-side delivery latency.

For implementation guidance, see Track OTP success with AWS End User Messaging SMS feedback.

Step 10: Set up cost visibility

SMS costs include AWS charges plus per-message carrier surcharges. Setting up cost visibility from day one lets you track spend trends, catch anomalies early, and optimize over time.

Start by activating AWS Cost Explorer and creating a cost allocation tag for your SMS workload. Then configure an AWS Budget with threshold alerts. For example, you might notify at 80% of projected monthly spend. This gives you advance warning of unexpected cost increases, whether from traffic spikes, retry loops, or blocked-country leakage.

Step 11: Plan your traffic migration

A phased rollout validates delivery performance at each stage before you increase volume.

Start with a canary phase (Day 1 to 3) where you route 5 to 10% of traffic to the short code and monitor delivery rates, latency, and event logs. Move to a ramp phase (Day 3 to 7) at 50%, validating throughput and carrier-level delivery across your recipient base. Finish the full migration (Day 7+) at 100%. Decommission your previous origination identity only after confirming stability for at least 48 hours.

Step 12: Validate production readiness and send

Before declaring your short code production-ready, run through the following validation checks:

  1. Confirm your CloudWatch dashboard shows events flowing for TEXT_DELIVERED and TEXT_FAILED (from Step 3).
  2. Send a test message that triggers your STOP keyword. Verify the correct opt-out response is returned and the phone number appears in your opt-out list.
  3. Send a test message that triggers your HELP keyword. Verify the response matches your approved registration.
  4. Check your MPS quota in the support case response (from Step 5). Confirm it matches or exceeds your calculated peak.
  5. Review your country restrictions (from Step 7). Attempt to send a message to a blocked country and confirm it is rejected.
  6. Verify your CloudWatch alarm fires by temporarily lowering the threshold, or by checking that the alarm state is not INSUFFICIENT_DATA.

After all six checks pass, you are ready to begin your phased migration (Step 11) and scale to full production traffic. At this point, your short code is configured, monitored, compliant, and protected.

Automate with a validation script

You can use an AI coding assistant such as Kiro to generate a validation script tailored to your environment. Try a prompt like: “Write a boto3 script that validates my SMS short code is production-ready by checking Active status, HELP/STOP keywords, configuration set existence, and pool association using the pinpoint-sms-voice-v2 client.”

Refine the prompt with specifics from the following reference implementation, such as exact API names, filter parameters, and output format, to match your requirements.

The following script is an example of what that output looks like:

import boto3
import sys

SHORT_CODE = "12345"  # TODO: Replace with your short code (e.g., "67890")
POOL_ID = "pool-1234567890abcdef0"  # TODO: Replace with your pool ID from Step 4
CONFIG_SET_NAME = "prod-otp-shortcode"  # TODO: Replace with your configuration set name from Step 3

client = boto3.client("pinpoint-sms-voice-v2")

# Note: For accounts with many resources, implement NextToken pagination
# on describe_* calls. This script assumes results fit in a single page.


def check_short_code_active():
    """Step 1: Verify short code is Active."""
    response = client.describe_phone_numbers(
        Filters=[
            {"Name": "status", "Values": ["ACTIVE"]},
            {"Name": "number-type", "Values": ["SHORT_CODE"]}
        ]
    )
    numbers = [
        n for n in response["PhoneNumbers"]
        if n["PhoneNumber"] == SHORT_CODE
    ]
    assert len(numbers) > 0, f"Short code {SHORT_CODE} not found or not Active"
    print(f"[PASS] Short code {SHORT_CODE} is Active")


def check_keywords_configured():
    """Step 2: Verify HELP and STOP keywords exist."""
    response = client.describe_keywords(OriginationIdentity=SHORT_CODE)
    keyword_names = [kw["Keyword"].upper() for kw in response["Keywords"]]
    assert "STOP" in keyword_names, "STOP keyword not configured"
    assert "HELP" in keyword_names, "HELP keyword not configured"
    print("[PASS] HELP and STOP keywords configured")


def check_configuration_set():
    """Step 3: Verify configuration set exists."""
    response = client.describe_configuration_sets(
        ConfigurationSetNames=[CONFIG_SET_NAME]
    )
    assert len(response["ConfigurationSets"]) > 0, f"Configuration set {CONFIG_SET_NAME} not found"
    print(f"[PASS] Configuration set '{CONFIG_SET_NAME}' exists")


def check_pool_association():
    """Step 4: Verify pool exists and short code is associated to it."""
    response = client.describe_pools(PoolIds=[POOL_ID])
    assert len(response["Pools"]) > 0, f"Pool {POOL_ID} not found"

    # Verify short code is associated to the pool
    assoc_response = client.list_pool_origination_identities(PoolId=POOL_ID)
    identities = [
        oi["OriginationIdentity"]
        for oi in assoc_response["OriginationIdentities"]
    ]
    assert any(SHORT_CODE in oi for oi in identities), \
        f"Short code {SHORT_CODE} not associated with pool {POOL_ID}"
    print(f"[PASS] Pool '{POOL_ID}' exists and short code is associated")


if __name__ == "__main__":
    checks = [
        check_short_code_active,
        check_keywords_configured,
        check_configuration_set,
        check_pool_association,
    ]
    for check in checks:
        try:
            check()
        except Exception as e:
            print(f"[FAIL] {check.__doc__} - {e}")
            sys.exit(1)
    print("\nAll validation checks passed. Ready for production traffic.")

Cleaning up

If you created test resources while following this walkthrough, you can delete them through the AWS End User Messaging SMS console or with the API to avoid confusion with your production configuration. This includes a test configuration set, test pool, or test event destinations used for validation. Do not delete your production configuration set, pool, or keyword settings.

If you requested a test-level MPS increase or spending limit for validation, update these to your production values through a new support case before going live.

Quick reference checklist

Step Action Key API / Service
1 Verify short code is Active and test delivery SendTextMessage
2 Configure keywords and verify message compliance PutKeyword
3 Create configuration set with event destinations CreateConfigurationSet
4 Create pool and associate short code CreatePool, AssociateOriginationIdentity
5 Request MPS increase for expected throughput AWS Support
6 Request spending limit increase AWS Support
7 Restrict destination countries Console / UpdateAccount
8 Set up CloudWatch alarms and dashboards Amazon CloudWatch
9 Track OTP verification success (if applicable) SMS Feedback events
10 Set up cost visibility AWS Cost Explorer, AWS Budgets
11 Plan phased traffic migration Application-level routing
12 Validate production readiness and send All of the preceding

Conclusion

In this post, we walked through how to configure a newly provisioned SMS short code for production use with AWS End User Messaging SMS. The 12 steps cover keyword verification, message compliance, event monitoring, throughput planning, country restrictions, cost visibility, phased traffic migration, and a final production validation.

You can adapt the sequence to your specific use case and volume profile. For the full registration and application process, see A guide to SMS short codes with AWS End User Messaging. To start configuring, navigate to the AWS End User Messaging SMS console. For the full API reference, see the AWS End User Messaging SMS documentation.


About the author

Adding LINE Messenger to your AWS omnichannel fallback solution

Post Syndicated from Rommel Sunga original https://aws.amazon.com/blogs/messaging-and-targeting/adding-line-messenger-to-your-aws-omnichannel-fallback-solution/

In this post, you will learn how to extend an existing omnichannel fallback solution by integrating LINE Messenger, including architecture updates, deployment steps, and testing procedures. The original solution, built with Amazon API Gateway, AWS Lambda, Amazon Simple Email Service (Amazon SES), and AWS End User Messaging, delivers messages across SMS, WhatsApp, and email with automatic fallback capabilities.

For more information about the original omnichannel fallback solution that this aims to extend to LINE, see the Enhancing Message Reach: An Omnichannel Approach Using WhatsApp, SMS, and Email with AWS.

Why LINE Messenger?

LINE is a popular messaging platform in Japan, Taiwan, and Thailand, with 181 million monthly active users across its primary markets, including 100 million in Japan alone (LY Corporation FY2025 Q3 earnings data). With an 84 percent DAU/MAU ratio (88 percent in Japan), LINE sees high daily engagement, making it a reliable channel for time-sensitive communications such as appointment reminders in healthcare, order and shipping notifications in ecommerce, and promotional campaigns in retail.

While other messaging platforms are popular in specific APAC markets (KakaoTalk in South Korea, WeChat in China, Zalo in Vietnam, Viber in the Philippines), LINE holds a strong position across Japan, Taiwan, and Thailand simultaneously, making it a high-impact addition to a multi-channel messaging strategy for those countries. By adding LINE to the omnichannel fallback solution, you can reach your audience on their preferred messaging channel in these key markets. You can use LINE as either a primary or fallback channel while maintaining the same fallback and broadcast patterns already available for other channels.

Cost note: LINE Messaging API pricing varies by country and plan. See the pricing pages for LINE Messaging API, Amazon Simple Email Service (Amazon SES), and Amazon End User Messaging for details on each channel.

Architecture overview

Adding LINE to your fallback solution means you can now cover four major messaging channels from a single API endpoint, giving you broader reach without added operational complexity. The LINE integration follows the same event-driven serverless pattern as the existing channels. The following diagram shows the key additions to the architecture.

Figure 1: Updated omnichannel architecture with LINE Messenger (new components highlighted)

You can now reach LINE users with two straightforward additions to the existing architecture:

LINE Messaging API Integration – The Primary and Secondary Handler Lambda functions now include a send_line module that calls the LINE Messaging API to deliver messages using the Push Message endpoint.

AWS Secrets Manager Integration – LINE channel credentials (access token and channel secret) are stored securely in AWS Secrets Manager and retrieved by Lambda functions with caching for performance.

How LINE integration works

The LINE Messenger integration extends the existing message processing pipeline, so you get the same reliable fallback behavior that you already have for email, SMS, and WhatsApp. The following sections describe how the system handles LINE messages and fallback scenarios.

Sending a LINE message

When you send a message with LINE as the primary or fallback channel, the flow follows the same pattern as other channels with LINE-specific handling:

  1. API Gateway receives the request and places it in the Primary Amazon Simple Queue Service (Amazon SQS) Queue.
  2. The Primary Handler Lambda detects the channel as “line” and invokes the send_line module.
  3. The send_line module retrieves LINE credentials from Secrets Manager (cached for performance) and sends a request to the LINE Messaging API Push Message endpoint. The Push Message API sends messages to LINE users without requiring the user to message first. The request body contains a to field with the recipient’s LINE User ID (a unique identifier assigned when a user follows your LINE Official Account) and a messages array with the message objects to deliver. The module validates the recipient LINE User ID against the expected format (a capital ‘U’ followed by 32 lowercase hexadecimal characters) before invoking the LINE API. Requests with malformed recipient IDs are rejected early and don’t reach the external API.
  4. The Lambda function records the message status in the Amazon DynamoDB table.
  5. If fallback is configured, the Lambda function enqueues the message to the Fallback Queue. This happens regardless of whether the LINE API call succeeds (HTTP 200) or fails (non-200 response, timeout, or exception). DynamoDB records the message status as delivered on success or failed on failure. The Secondary Handler checks DynamoDB and sends through the fallback channel if the status isn’t delivered.
  6. The Secondary Handler updates the DynamoDB status to sent_fallback.

How LINE differs from other channels

Aspect Email SMS WhatsApp LINE
API Amazon SES SendEmail API AWS End User Messaging SendTextMessage API AWS End User Messaging Social SendWhatsAppMessage API LINE Messaging API Push Message API
Authentication IAM roles IAM roles IAM roles Channel access token via Secrets Manager
External Message ID Mapping Not required. SES returns the same message ID in delivery callbacks Not required. SMS returns the same message ID in delivery callbacks. Required. WhatsApp returns a different platform message ID in delivery webhooks that must be mapped back to the internal AWS message ID. Not required. No delivery callbacks exist, so no message ID correlation is needed.
Credential Storage IAM (automatic) IAM (automatic) IAM (automatic) Secrets Manager (manual)
Delivery Tracking Async via SES delivery events (SNS callback updates DynamoDB) Async via End User Messaging events (SNS callback updates DynamoDB) Async via End User Messaging events (SNS callback updates DynamoDB) None. Status set to delivered immediately on 200 response from LINE API. No delivery webhook available for LINE Messaging API.

LINE uses an external API with its own authentication rather than AWS-native IAM authentication. This means you must manage credentials through AWS Secrets Manager rather than relying on AWS Identity and Access Management (IAM)-managed authentication. For more information, see the LINE Messaging API documentation.

LINE offers two distinct messaging products for businesses, LINE Messaging API, and LINE Official Notification.

  • The LINE Messaging API, which is the focus of this guide, supports two-way conversational messaging and is widely adopted across industries for use cases such as mobile ordering, loyalty programs, and customer engagement. LINE also offers LINE Official Notification (also known as LINE Notification Messages), a separate service designed for one-way transactional notifications such as shipping updates and appointment reminders, which requires business verification.
  • LINE Official Notification provides per-message delivery completion events, but the LINE Messaging API doesn’t. With the Messaging API, an HTTP 200 response confirms LINE accepted the message for delivery, and this is the most granular delivery signal available.

Creating a LINE Messaging API Channel

You need a LINE Messaging API Channel to authenticate and send messages through the LINE integration. The following steps walk you through creating one:

  1. Sign in to the LINE Developers Console. Create a personal LINE account if you don’t have one already and download the corresponding iOS/Android/PC application. This is required to test receiving LINE messages.
  2. Create a Provider (your company/org name).
  3. Create a new Messaging API channel under that provider.
  4. After you create the channel, enable the Messaging API from the LINE Official Account Manager page.
  5. From the channel settings, note the following:
    1. Channel access token (Messaging API tab, then select Issue)

    2. Channel secret (Basic settings tab)
  6. Disable Auto-reply and Greeting messages under Messaging API settings.

Deploying and testing

The repository includes a complete deployment guide with step-by-step instructions for deploying the CDK stack, configuring LINE credentials in AWS Secrets Manager, obtaining personal LINE user IDs, and running the integration test suite. The test suite automatically detects which channels are configured and runs the applicable tests. For full deployment and testing instructions, see the Deployment Guide in the repository.

Security considerations

Before deploying this solution to production, review the following considerations and adjust for your workload and compliance obligations.

Least-privilege IAM

The Lambda execution roles in the sample scope DynamoDB, Amazon SQS, and AWS Secrets Manager permissions to specific resource ARNs. The send actions for Amazon SES (ses:SendEmail, ses:SendTemplatedEmail), SMS (sms-voice:SendTextMessage), and WhatsApp (social-messaging:SendWhatsAppMessage) are granted on resources: [“*”] in this sample for simplicity, because the specific sending identities, phone pools, and WhatsApp business accounts are left configurable. For production, scope these further where the API supports it: SES allows identity-level ARNs (for example, arn:aws:ses:region:account:identity/example.com), and End User Messaging SMS supports pool and phone-number ARNs. When adapting this code, keep resource-level scoping for everything that supports it and review the AWS Well-Architected Security Pillar and Lambda execution role guidance for production deployments.

Rotating LINE credentials

LINE channel access tokens are long-lived and are issued and rotated manually through the LINE Developers Console; there’s no programmatic rotation API. Rotate the token periodically in line with your organization’s key-rotation policy (for example, every 90 days), update the Secrets Manager secret with the new value, and force a Lambda cold start (by redeploying the stack or updating a Lambda environment variable) so the cached credentials are refreshed.

Data protection and PII retention

The solution stores message metadata and recipient identifiers (including LINE User IDs, phone numbers, and email addresses) in Amazon DynamoDB. DynamoDB uses AWS-managed encryption at rest, Secrets Manager uses AWS Key Management Service (AWS KMS), and all outbound calls to the LINE API are made over HTTPS. Point-in-time recovery is enabled on the message table.

The sample doesn’t configure a DynamoDB Time-to-Live (TTL) attribute, so records persist indefinitely. For production, add a TTL attribute (for example, expiresAt) that matches your retention policy, and review whether the RemovalPolicy.RETAIN setting on the tables is appropriate for your environment. LINE User IDs, phone numbers, and email addresses are personally identifiable information under regulations including Japan’s APPI, the EU’s GDPR, and similar laws. Assess your retention obligations, data residency requirements, and processes for handling subject access and deletion requests for the regions you serve.

Conclusion

By adding LINE Messenger to the omnichannel fallback solution, you can now reach your customers across the four messaging channels that matter most: email, SMS, WhatsApp, and LINE. The integration follows the same serverless, event-driven patterns as the existing channels, making it straightforward to deploy and maintain. LINE can serve as either a primary or fallback channel, giving you the flexibility to tailor your messaging strategy to regional preferences. As a next step, consider adding other regional messaging services to further expand your reach. You can also explore advanced LINE features such as rich messages, quick replies, and Flex Messages to create more engaging customer interactions.

Resources


About the authors

Send WhatsApp Business messages with AWS End User Messaging Social

Post Syndicated from Rommel Sunga original https://aws.amazon.com/blogs/messaging-and-targeting/send-whatsapp-business-messages-with-aws-end-user-messaging-social/

WhatsApp reaches over 3 billion monthly active users worldwide — with more than 2 billion using it every day — making it the single most direct channel for customer communication at global scale. AWS End User Messaging Social gives you a managed API to send WhatsApp Business messages without building or maintaining your own WhatsApp Business API integration.

Whether you’re building appointment reminders, order notifications, or interactive customer support flows, with WhatsApp Business messaging, you can meet customers where they already communicate.

This post is for developers and solutions architects looking to integrate WhatsApp messaging into their customer engagement workflows using AWS. By the end, you will know how to send every supported message type—from straightforward text and media to templates, interactive menus, and WhatsApp Flows—using Python and the AWS End User Messaging Social API.

Solution overview

This solution uses AWS End User Messaging Social to connect your WhatsApp Business Account (WABA) with your AWS workloads, enabling automated, two-way messaging with your customers. When a customer sends a WhatsApp message to your WABA phone number, AWS End User Messaging Social receives the message and publishes an event to an Amazon Simple Notification Service (Amazon SNS) topic. Amazon SNS then invokes an AWS Lambda function. You configure this Lambda function to process the incoming message and send a contextual WhatsApp reply to the customer. The following diagram illustrates this architecture:

Figure A: Shows the architecture flow “Customer WhatsApp → End User Messaging Social → SNS → Lambda → End User Messaging Social → Response”

IMPORTANT: Note that any WhatsApp user can send a message to your WABA number and trigger the workflow. To avoid incurring ongoing charges, complete the steps in the “Clean up” section.

The solution is deployed using the AWS Serverless Application Model (AWS SAM) and includes a sample Lambda function that demonstrates how to handle common message types, send interactive list messages, and respond with templated replies. You can extend this foundation to build more sophisticated workflows, such as invoking AWS Step Functions for multi-step processes or routing messages to container-based workloads running on AWS. AWS End User Messaging Social also provides unified billing within AWS, streamlining cost management for your WhatsApp messaging workloads.

Prerequisites

Before you run any of the examples in this post, complete the following prerequisites:

  • A phone number to link a WABA.
  • Connect a verified WhatsApp Business Account (WABA) to AWS End User Messaging Social.
  • Install the AWS SDK for Python (Boto3).
  • Optionally, install the AWS Command Line Interface (AWS CLI) for template creation (otherwise the templates must be created within the AWS Console UI or Meta’s WhatsApp Manager).
  • A phone with WhatsApp Messenger app installed to test the solution. Note that the mobile app uses a different phone number than the one that is associated with your WABA.

This walkthrough typically takes 30–45 minutes, though actual time might vary based on your familiarity with AWS services and WhatsApp Business configuration.

If you’re new to AWS End User Messaging Social, see the Getting Started with WhatsApp guide before proceeding.

IAM permissions

To configure AWS End User Messaging Social, you will need several types of permissions depending on what you’re setting up. Here are the key permissions required:

Core AWS End User Messaging social permissions

For basic configuration and management, you will need permissions for the following actions:

  • social-messaging:AssociateWhatsAppBusinessAccount — to associate a WhatsApp Business Account with your AWS account
  • social-messaging:CreateWhatsAppMessageTemplate — to create WhatsApp message templates
  • social-messaging:ListLinkedWhatsAppBusinessAccounts — to list linked WhatsApp Business Accounts
  • social-messaging:GetWhatsAppMessageTemplate — to retrieve message template details
  • social-messaging:ListWhatsAppMessageTemplates — to list message templates

For event destinations (SNS integration)

If you’re configuring event destinations with Amazon SNS, your SNS topic must have a resource policy allowing the sms-voice.amazonaws.com service principal to publish to it, and you will need an AWS Identity and Access Management (IAM) role with a trust policy allowing social-messaging.amazonaws.com to assume it. For detailed setup instructions, see Configuring Event Destinations in the AWS End User Messaging Social User Guide.

For Amazon Connect integration

If integrating with Amazon Connect, the service-linked role (SLR) includes the following permissions:

  • social-messaging:SendWhatsAppMessage
  • social-messaging:PostWhatsAppMessageMedia
  • social-messaging:GetWhatsAppMessageMedia
  • social-messaging:GetLinkedWhatsAppBusinessAccountPhoneNumber

For more information, see Amazon Connect Service-Linked Role in the Amazon Connect Administrator Guide.

Additional considerations

  • If using encrypted SNS topics with AWS KMS, you will need additional AWS Key Management Service (AWS KMS) permissions for the service to generate and decrypt data keys.

Configuration

Create a config.json file in your project directory with the following structure to store the destination WhatsApp number and the origination phone number ID (for example, phone-number-id-a1b2c3d4######) that we’re sending messages from and receiving the message:

{
    "originationPhoneNumberId": "your-whatsapp-phone-number-id",
    "destinationPhoneNumber": "+1234567890"
}

Security note: The config.json file is intended for local testing only. In production environments, avoid hardcoding phone numbers and credentials. Instead, use AWS Secrets Manager, AWS Systems Manager Parameter Store, or environment variables to manage sensitive configuration values.

Each example builds a message_data payload and sends it using the following code:

import json
import boto3
import os

config_path = os.path.join(os.path.dirname(__file__), 'config.json')
with open(config_path, 'r') as f:
    config = json.load(f)

client = boto3.client('socialmessaging')
message_data = { ... }

response = client.send_whatsapp_message(
    message=json.dumps(message_data),
    originationPhoneNumberId=config['originationPhoneNumberId'],
    metaApiVersion="v20.0"
)
print(f"Message sent successfully! Response: {response}")

For production use, wrap the send call with error handling to manage throttling and failures gracefully:

try:
    result = client.send_whatsapp_message(
        message=json.dumps(message_data),
        originationPhoneNumberId=config['originationPhoneNumberId'],
        metaApiVersion="v20.0"
    )
    print(f"Message sent. ID: {result['messageId']}")
except client.exceptions.ThrottlingException as e:
    print(f"Rate limited. Retry after backoff: {e}")
except Exception as e:
    print(f"Failed to send message: {e}")

The following sections show only the message_data for each message type. To send any of these messages, use the shared sending code from the previous Configuration section—load config.json, create the Boto3 client, and call client.send_whatsapp_message() with the payload.

Text messages

Text messages are plain text communications sent within the 24-hour conversation window. They don’t require Meta template approval and support basic formatting.

Specifications and requirements

Text messages are the foundation of WhatsApp conversations. You can send up to 4,096 characters per message. Text messages don’t require Meta approval and work within an active 24-hour conversation window that opens when a customer messages your business first, or when you initiate contact using an approved template.

  • Maximum length: 4,096 characters (including spaces and formatting)
  • 24-hour window requirement: Can only be sent within 24 hours of last customer message
  • No WhatsApp template approval required

WhatsApp message example

The following image is a WhatsApp message example.

Code example

The following is a code example for the previous message.

message_data = {
    "messaging_product": "whatsapp",
    "to": config['destinationPhoneNumber'],
    "type": "text",
    "text": {
        "body": "For the Classic T-Shirt, we recommend size Medium based on your measurements. It runs true to size with a regular fit. Size Medium: Chest 38-40 inches, Length 28 inches."
    }
}

Media messages

With Media messages, you can send images, documents, audio, and video files.

Specifications and requirements

  • Images: Maximum 5 MB (JPEG, PNG)
  • Documents: Maximum 100 MB (PDF, DOC, DOCX, PPT, PPTX, XLS, XLSX)
  • Audio: Maximum 16 MB (AAC, MP4, AMR, MP3, OGG)
  • Video: Maximum 16 MB (MP4, 3GPP)

Note: Media uploaded to Meta is retained for 30 days. If you reuse media across messages (such as a company logo), use the external URL method or re-upload before sending. The examples in this post use external URLs.

Media (document) message example

The message is presented as a PDF with the provided message body in the following example.

Code example

The following is a code example for the previous message.

message_data = {
    "messaging_product": "whatsapp",
    "to": config['destinationPhoneNumber'],
    "type": "document",
    "document": {
        "link": "https://docs.aws.amazon.com/pdfs/social-messaging/latest/userguide/social-ug.pdf",
        "caption": "\U0001F4C4 Your invoice #12345 is ready. Payment due: January 31, 2024. Questions? Reply to this message.",
        "filename": "Invoice-12345.pdf"
    }
}

Media (image) message example

This media message includes an image with a message body in the following example to describe the image. In this case it shows a laptop available for purchase with a link included as part of the message body.

Code example

The following is a code example for the previous message.

message_data = {
    "messaging_product": "whatsapp",
    "to": config['destinationPhoneNumber'],
    "type": "image",
    "image": {
            "link": "https://signin.aws.amazon.com/v2/assets/_next/static/media/[email protected]",
            "caption": "Thanks for reaching out to AnyCompany! Here's the product image for the laptop you inquired about. Feel free to reply if you need more details."
    }
}

Template messages

Template messages enable business-initiated conversations outside the 24-hour window. Templates must be created and approved by Meta before use.

Text template message creation with parameters

Template creation using CLI

While it’s possible to create templates directly in the AWS End User Messaging Social console and in WhatsApp Manager’s UI, you can also use AWS End User Messaging Social to programmatically create templates using the AWS CLI.

Step 1 — Create a file named order_confirmation_template.json in your working directory with the following template definition.

{
    "name": "order_confirmation_template",
    "language": "en",
    "category": "UTILITY",
    "parameter_format": "named",
    "components": [
      {
        "type": "HEADER",
        "format": "TEXT",
        "text": "Order {{order_number}}",
        "example": {
          "header_text_named_params": [
            {"param_name": "order_number", "example": "ORD-2025-001"}
          ]
        }
      },
      {
        "type": "BODY",
        "text": "Hi {{customer_name}},\n\nYour order has been confirmed!\n\nItem: {{item_name}}\nTotal: {{total_amount}}\nEstimated Delivery: {{delivery_date}}\n\nThank you for shopping
  with us!",
        "example": {
          "body_text_named_params": [
            {"param_name": "customer_name", "example": "John Smith"},
            {"param_name": "item_name", "example": "Wireless Headphones"},
            {"param_name": "total_amount", "example": "$179.17"},
            {"param_name": "delivery_date", "example": "November 30, 2025"}
          ]
        }
      }
    ]
  }

Step 2 — Run the following CLI command to create the template. Note that the –template-definition parameter points to the local JSON file that contains the template structure:

aws socialmessaging create-whatsapp-message-template \

    --region [AWS_REGION] \
    --cli-binary-format raw-in-base64-out \
    --id [WABA_ID] \
    
--template-definition file://order_confirmation_template.json

Template creation using the console

AWS End User Messaging Social also features a built-in template management UI which you can use to create and manage templates directly within the AWS End User Messaging Social portion of the AWS Console under AWS End User messaging > Social Messaging > Message Templates. To create a new template, choose the Create Template button.

In the first screen, you can set your template name, template language and template type for your respective WhatsApp Business Account.

In the second screen, you can set the message content for the template including variables to dynamically populate when sending the message content.

Approved message templates can be seen under the message template screen along with their approval status.

Sending the template message

After your template is approved, you can send it with parameter values populated at send time.

Important: When sending the template message, you must use the same language and locale that you used when creating the template. For example, a language code of “en” isn’t the same as “en_US”.

message_data = {
    "messaging_product": "whatsapp",
    "to": config['destinationPhoneNumber'],
    "type": "template",
    "template": {
        "name": "order_confirmation_template",
        "language": {"code": "en"},
        "components": [
            {
                "type": "header",
                "parameters": [
                    {"type": "text", "parameter_name": "order_number", "text": "ORD-2025-001"}
                ]
            },
            {
                "type": "body",
                "parameters": [
                    {"type": "text", "parameter_name": "customer_name", "text": "John Smith"},
                    {"type": "text", "parameter_name": "item_name", "text": "Wireless Headphones"},
                    {"type": "text", "parameter_name": "total_amount", "text": "$179.17"},
                    {"type": "text", "parameter_name": "delivery_date", "text": "November 30, 2025"}
                ]
            }
        ]
    }
}

Location messages

Location messages share geographic coordinates with optional name and address information. For example, you can send a location for a store branch to a customer.

Create template message with CLI

Step 1 — Create a file named store_location_template.json in your working directory with the following template definition:

{
    "name": "store_location",
    "language": "en",
    "category": "UTILITY",
    "parameter_format": "named",
    "components": [
      {
        "type": "HEADER",
        "format": "LOCATION"
      },
      {
        "type": "BODY",
        "text": "Visit our {{store_name}} store!\n\nOperating Hours: {{hours}}\n\nSee you soon!",
        "example": {
          "body_text_named_params": [
            {"param_name": "store_name", "example": "Marina Bay Store"},
            {"param_name": "hours", "example": "10 AM - 10 PM"}
          ]
        }
      }
    ]
  }

Step 2 — Run the following CLI command to create the template. The –template-definition parameter references the JSON file that you previously created:

aws socialmessaging create-whatsapp-message-template \
    --region [AWS_REGION] \
    --cli-binary-format raw-in-base64-out \
    --id [WABA_ID] \

--template-definition file://store_location_template.json

Location message example

The following image is an example of a location message.

Code example

The following is a code example of the previous location message example.

message_data = {
    "messaging_product": "whatsapp",
    "to": config['destinationPhoneNumber'],
    "type": "template",
    "template": {
        "name": "store_location",
        "language": {
            "code": "en"
        },
        "components": [
            {
                "type": "header",
                "parameters": [
                    {
                        "type": "location",
                        "location": {
                            "latitude": "1.2838",
                            "longitude": "103.8591",
                            "name": "Marina Bay Sands",
                            "address": "10 Bayfront Ave, Singapore 018956"
                        }
                    }
                ]
            },
            {
                "type": "body",
                "parameters": [
                    {
                        "type": "text",
                        "parameter_name": "store_name",
                        "text": "Marina Bay Store"
                    },
                    {
                        "type": "text",
                        "parameter_name": "hours",
                        "text": "10 AM - 10 PM"
                    }
                ]
            }
        ]
    }
}

Interactive messages

Interactive messages provide pre-defined response options through buttons or lists. These messages must be sent within the 24-hour conversation window.

  • Structured responses – Pre-defined options ensure consistent data collection
  • Enhanced UX – One-tap interactions reduce friction and improve completion rates
  • 24-hour window requirement – Must be sent within active conversation windows

Quick reply buttons message

This example demonstrates collecting guest feedback using quick reply buttons for streamlined response collection.

Technical specifications:

  • Maximum three buttons per message
  • Button text limit: 20 characters
  • Buttons appear in a horizontal row on most devices

Quick reply buttons message example

The following image is an example of the Quick reply buttons message.

Code example

The following is a code example of the previous example image.

message_data = {
    "messaging_product": "whatsapp",
    "recipient_type": "individual",
    "to": config['destinationPhoneNumber'],
    "type": "interactive",
    "interactive": {
        "type": "button",
        "body": {
            "text": "\U0001F3E8 Thank you for staying with us! How would you rate your experience?"
        },
        "action": {
            "buttons": [
                {
                    "type": "reply",
                    "reply": {
                        "id": "excellent",
                        "title": "\u2B50 \u2B50 \u2B50 \u2B50 \u2B50 Excellent"
                    }
                },
                {
                    "type": "reply",
                    "reply": {
                        "id": "good",
                        "title": "\u2B50 \u2B50 \u2B50 \u2B50 Good"
                    }
                },
                {
                    "type": "reply",
                    "reply": {
                        "id": "needs_improvement",
                        "title": "\u2B50 \u2B50 Needs Work"
                    }
                }
            ]
        }
    }
}

List Message

This example shows a restaurant menu using list messages for organized, scrollable content presentation.

Technical specifications:

  • Max Options: Up to 10 rows total, which can be organized into 10 sections.
  • Button Text: Maximum 24 characters to open the menu.
  • Sections: A list can contain multiple sections (e.g., “Starters”, “Mains”).

List message example

The following is an image of a list message example.

When the user expands the list, they can select individual items to add to their cart.

Code example

The following is the code example for the previously mentioned example.

message_data = {
    "messaging_product": "whatsapp",
    "recipient_type": "individual",
    "to": config['destinationPhoneNumber'],
    "type": "interactive",
    "interactive": {
        "type": "list",
        "body": {
            "text": "\U0001F37D\uFE0F Welcome to Bella Vista! Browse our menu categories in the following list:"
        },
        "action": {
            "button": "View Menu",
            "sections": [
                {
                    "title": "Main Courses",
                    "rows": [
                        {
                            "id": "pasta",
                            "title": "\U0001F35D Pasta Dishes",
                            "description": "Fresh pasta with signature sauces"
                        },
                        {
                            "id": "pizza",
                            "title": "\U0001F355 Wood-Fired Pizza",
                            "description": "Authentic Italian pizza"
                        }
                    ]
                },
                {
                    "title": "Beverages",
                    "rows": [
                        {
                            "id": "wine",
                            "title": "\U0001F377 Wine Selection",
                            "description": "Curated Italian wines"
                        },
                        {
                            "id": "cocktails",
                            "title": "\U0001F378 Signature Cocktails",
                            "description": "Handcrafted cocktails"
                        }
                    ]
                }
            ]
        }
    }
}

Contact messages

Contact messages share vCard-formatted contact information, useful for sharing business contact details with customers.

Contact message example

The following is an image of an example contact message.

Customers can choose to directly message the contact, save the contact to their contacts list and view details about the contact in a pop up.

Code example

The following is a code example for the previously mentioned image example.

message_data = {
    "messaging_product": "whatsapp",
    "to": config['destinationPhoneNumber'],
    "type": "contacts",
    "contacts": [
        {
            "name": {
                "formatted_name": "AnyCompany Customer Support",
                "first_name": "Customer",
                "last_name": "Support"
            },
            "org": {
                "company": "AnyCompany Inc."
            },
            "phones": [
                {
                    "phone": "+1-555-0123",
                    "type": "WORK",
                    "wa_id": "15550123"
                }
            ],
            "emails": [
                {
                    "email": "[email protected]",
                    "type": "WORK"
                }
            ],
            "urls": [
                {
                    "url": "https://www.anycompany.com",
                    "type": "WORK"
                }
            ],
            "addresses": [
                {
                    "street": "123 Business Ave",
                    "city": "Seattle",
                    "state": "WA",
                    "zip": "98101",
                    "country": "United States",
                    "country_code": "US",
                    "type": "WORK"
                }
            ]
        }
    ]
}

WhatsApp Flows

WhatsApp Flows can help power interactive experiences. Note that a verified WhatsApp Business Account is required to use WhatsApp Flows. There are two general types of WhatsApp Flows:

  • Without an endpoint — Results are stored and can be viewed directly within WhatsApp Manager. This is the type demonstrated in this post.
  • With an endpoint — Results are sent to an API endpoint that you specify for further processing.

WhatsApp Flow – survey

This is an example of a WhatsApp Flow without an endpoint that sends a product survey to customers. Since this flow doesn’t use an endpoint, the results can be viewed directly within WhatsApp Manager.

Create WhatsApp Flow using Flow Playground

First create a WhatsApp Flow using the Flows Playground on the Meta for Developers website.

You can use the following JSON in the editor to use the example flow that we’re creating.

{"version":"7.2","screens":[{"id":"QUESTION_ONE","title":"Question 1 of 2","data":{},"layout":{"type":"SingleColumnLayout","children":[{"type":"Form","name":"form","children":[{"type":"TextHeading","text":"What product categories interest you?"},{"type":"CheckboxGroup","label":"Choose all that apply:","required":true,"name":"product_categories","data-source":[{"id":"electronics","title":"Electronics"},{"id":"fashion","title":"Fashion"},{"id":"home","title":"Home & Living"},{"id":"beauty","title":"Beauty"}]},{"type":"Footer","label":"Continue","on-click-action":{"name":"navigate","next":{"type":"screen","name":"QUESTION_TWO"},"payload":{"product_categories":"${form.product_categories}"}}}]}]}},{"id":"QUESTION_TWO","title":"Question 2 of 2","data":{"product_categories":{"type":"array","items":{"type":"string"},"__example__":[]}},"terminal":true,"success":true,"layout":{"type":"SingleColumnLayout","children":[{"type":"Form","name":"form","children":[{"type":"TextHeading","text":"What's your typical budget?"},{"type":"RadioButtonsGroup","label":"Choose one:","required":true,"name":"budget_range","data-source":[{"id":"under_50","title":"Under $50"},{"id":"50_150","title":"$50 - $150"},{"id":"over_150","title":"Over $150"}]},{"type":"Footer","label":"Submit","on-click-action":{"name":"complete","payload":{"product_categories":"${data.product_categories}","budget_range":"${form.budget_range}"}}}]}]}}]}

Afterwards you will be prompted to create the template to attach to the flow.

Create WhatsApp template using AWS CLI

Step 1 — Create a file named anycompany_survey_template.json in your working directory with the following template definition. Notice that the Flow JSON is embedded directly in the flow_json field of the button component:

{ "name": "anycompany_survey", "language": "en_US", "category": "MARKETING", "components": [ { "type": "BODY", "text": "Hi! We're AnyCompany and we'd love to personalize your shopping experience. Take our quick 2-question survey!" }, { "type": "BUTTONS", "buttons": [ { "type": "FLOW", "text": "Start Survey", "flow_json": "{"version":"7.2","screens":[{"id":"QUESTION_ONE","title":"Question 1 of 2","data":{},"layout":{"type":"SingleColumnLayout","children":[{"type":"Form","name":"form","children":[{"type":"TextHeading","text":"What product categories interest you?"},{"type":"CheckboxGroup","label":"Choose all that apply:","required":true,"name":"product_categories","data-source":[{"id":"electronics","title":"Electronics"},{"id":"fashion","title":"Fashion"},{"id":"home", "title":"Home & Living"},{"id":"beauty","title":"Beauty"}]},{"type":"Footer","label":"Continue","on-click-action":{"name":"navigate","next":{"type":"screen","name":"QUESTI ON_TWO"},"payload":{"product_categories":"${form.product_categories}"}}}]}]}},{"id":"QUESTION_TWO","title":"Question 2 of 2","data":{"product_categories":{"type":"array","items":{"type":"string"},"__example__":[]}},"terminal":true,"success":true,"layout":{"type":"SingleColumnLayout
 ","children":[{"type":"Form","name":"form","children":[{"type":"TextHeading","text":"What's your typical budget?"},{"type":"RadioButtonsGroup","label":"Choose one:","required":true,"name":"budget_range","data-source":[{"id":"under_50","title":"Under $50"},{"id":"50_150","title":"$50 - $150"},{"id":"over_150","title":"Over $150"}]},{"type":"Footer","label":"Submit","on-click-action":{"name":"complete","payload":{"product_categories":"${data.product_categories}","budget_range":"${form .budget_range}"}}}]}]}}]}", "flow_action": "navigate" } ] } ] }

Step 2 — Run the following CLI command to create the template. The –template-definition parameter references the JSON file that you previously created:

aws socialmessaging create-whatsapp-message-template \
--region [AWS_REGION] \
--cli-binary-format raw-in-base64-out \
--id [WABA_ID] \
--template-definition file://anycompany_survey_template.json

Sending the WhatsApp Flow

After the Template and Flow are approved, a message can be sent with the following code.

message_data = {
    "messaging_product": "whatsapp",
    "to": config['destinationPhoneNumber'],
    "type": "template",
    "template": {
        "name": "anycompany_survey",
        "language": {
            "code": "en_US"
        },
        "components": [
            {
                "type": "button",
                "sub_type": "flow",
                "index": "0",
                "parameters": [
                    {
                        "type": "action",
                        "action": {
                            "flow_token": "survey-12345",
                            "flow_action_data": {
                                "screen": "QUESTION_ONE"
                            }
                        }
                    }
                ]
            }
        ]
    }
}

WhatsApp Flow survey example

The Flow will appear with the message body specified and a link to start the survey.

The recipient can provide their answers on each page of the Flow which can be submitted at the end.

After providing inputs, the Flow will show a completed message.

Clean up

To avoid incurring future charges, delete the resources that you created during this walkthrough:

  1. In the AWS End User Messaging Social console, disassociate (unlink) the WhatsApp Business Account.
  2. Navigate to Message Templates and delete any test templates that you created.
  3. Delete any WhatsApp Flows you created in the Flow Playground on the Meta for Developers website.
  4. Review your Amazon CloudWatch Logs for any log groups created by the service and delete them if no longer needed.
  5. Optionally, if you provisioned a phone number in End User Messaging SMS just for WhatsApp release the phone number in End User Messaging SMS.

For templates created in WhatsApp Manager, you can also delete them directly from the WhatsApp Business Manager interface on the Meta for Developers website.

Conclusion

In this post, you learned how to send various WhatsApp Business message types using AWS End User Messaging Social, including text messages, media messages, template messages, location messages, interactive messages, contact messages, and WhatsApp Flows.

AWS End User Messaging Social provides a managed API that removes the complexity of maintaining your own WhatsApp Business API integration, so you can focus on building engaging customer experiences. The service handles the heavy lifting of managing WhatsApp Business API connections, template approvals, and message delivery, while providing you with a unified, consistent API interface.

Next steps

  • Explore advanced template features with dynamic content and media headers
  • Implement webhook handlers for incoming messages and delivery receipts
  • Set up automated message flows for common customer inquiries using Amazon EventBridge
  • Integrate with Amazon Connect for omnichannel customer support experiences

We’d love to hear about your experience! Share your use cases and implementation approaches in the comments.

Additional resources


About the authors

Upgrade business messaging with RCS on AWS

Post Syndicated from Brett Ezell original https://aws.amazon.com/blogs/messaging-and-targeting/upgrade-business-messaging-with-rcs-on-aws/

SMS remains a reliable workhorse for business-to-consumer reach, but it isn’t without its hurdles. Messages from unrecognized numbers are frequently ignored or flagged as spam, and the limitations of plain text can’t provide the interactive experiences modern customers expect. Rich Communication Services (RCS) on AWS End User Messaging addresses these challenges as the next generation of mobile messaging.

Before we get into the technical implementation, it is important to understand what RCS is, why it’s becoming the new standard for business-to-consumer (B2C) communication, and the strategic value it brings to your messaging stack.

The problem with traditional business messaging

Traditional SMS has long been confined to a “narrow lane” of one-directional alerts—think one-time passcodes (OTP) and basic shipment updates. Because these messages arrive from generic-looking short codes or long codes, recipients have no native way to verify the sender’s legitimacy. As a result, users often do the rational thing: they ignore the message or treat it with suspicion.

What is RCS?

RCS is the next-generation messaging protocol developed by the GSM Association (GSMA) to update traditional Short Message Service (SMS) and Multimedia Messaging Service (MMS). Unlike SMS, which relies on the cellular signaling channel, RCS is entirely IP-based, operating over data connectivity (Wi-Fi or mobile data). This shift allows RCS to bring high-resolution media and interactive capabilities directly to the default messaging application.

The core innovation is the RCS Agent—your verified sending identity. Instead of a random number, recipients see your brand name, logo, and a verified checkmark. This shift from “unknown sender” to “verified brand” transforms the recipient’s behavior from passive ignore to active engagement. When customers trust the sender, they stop only reading alerts and start completing workflows, asking questions, and engaging with AI-powered agents built on services like Amazon Bedrock.

The business case for RCS

We can see the future of RCS by looking at markets where over-the-top (OTT) apps like WhatsApp are dominant. In those regions, businesses use messaging for full-lifecycle order management, customer service, and complex scheduling. In markets without that OTT distribution, businesses have been stuck with one-way SMS notifications.

RCS levels this playing field. By bringing a branded, verified identity natively to the default messaging app, it opens up a range of interactive use cases previously reserved for dedicated apps or websites.

Where to start?

When evaluating RCS for your program, we recommend starting with your highest-volume transactional messages. These are often the easiest to migrate because they follow predictable templates. More importantly, they provide the most immediate ROI by maximizing the visibility of your verified identity across your largest customer touchpoints.

To illustrate the business impact across industries:

  • Ecommerce: Order confirmations arriving from a verified brand logo eliminate the “Is this legitimate?” hesitation customers have with SMS from generic numbers. Customers click tracking links confidently because they recognize the sender immediately.
  • Healthcare: Appointment reminders with verified provider identity reduce no-shows and eliminate verification calls. Patients respond more quickly to verified communications and handle appointment management through messaging rather than calling the office.
  • Financial services: Fraud alerts with verified bank identity increase response rates and reduce phishing confusion. Customers see their bank’s logo and verified badge and know the alert is legitimate — enabling faster fraud detection and prevention.

Prerequisites

Before you begin the registration process, make sure that you have the following prerequisites in place:

  • An active AWS account with billing configured.
  • Access to AWS End User Messaging.
  • AWS Identity and Access Management (IAM) permissions to create and manage RCS agents and origination identities.
  • Existing SMS infrastructure to serve as a fallback.
  • A planned timeline that accounts for carrier approval lead times, which vary by country and carrier.
  • A budget for registration and verification fees.

Timeline, planning, and costs

Adopting RCS requires careful planning for both timelines and budgets. Carrier approval timelines vary by country and carrier — approval is not instant. Plan and verify that your processes, such as opt-in consent collection and brand asset preparation, are in place well before your intended launch date.

Registration and usage fees also differ significantly by market. Currently, AWS End User Messaging supports RCS in the United States and Canada, with additional countries planned for future rollout.

  • United States: This market uses a per-segment (160-character) pricing model similar to SMS. It features a higher initial barrier to entry, including a one-time agent setup fee and an annual brand vetting fee.
  • Canada: Canada utilizes a distinct message-based model (“Basic” vs. “Single” messages) rather than segments. Notably, it currently lacks the one-time setup and annual vetting fees found in the US, though a monthly maintenance fee applies globally to all active agents.

Also note that RCS is billed only upon successful delivery, whereas SMS is charged at the time of the request. For the latest rates and a breakdown of carrier-specific content violation fees, see AWS End User Messaging pricing.

Note: You are charged only for successfully delivered messages, not delivery attempts. In the United States, long messages are billed per 160-character segment; however, for the Rest of the World (ROW), messages exceeding 160 characters are billed as a single ‘RCS Single’ message. When automatic fallback occurs, you are typically charged only for the successful SMS delivery. While rare, note that if both the RCS and SMS messages reach the device (dual-delivery), charges for both may apply. For more details, see the RCS billing and pricing model.

RCS and SMS: Better together

RCS works alongside SMS to create a reliable messaging solution with automatic SMS fallback. AWS End User Messaging ensures reliable delivery by intelligently handling three common scenarios where RCS may be unavailable, triggering an automatic fallback to SMS:

  • Carrier-specific availability — Your RCS agent may be approved on some carriers but still pending on others, or a carrier may not have deployed RCS infrastructure yet. AWS detects this upfront using carrier lookup data and automatically routes via SMS so that the message is delivered.
  • Device compatibility — Not all devices support RCS, even if the carrier does. This includes older Android models, devices with RCS disabled, or iPhones running versions earlier than iOS 18. AWS detects this compatibility upfront where possible and automatically routes the message via SMS so that it reaches the recipient.
  • Temporary connectivity — A device may support RCS but lack data connectivity at the moment of delivery (for example, traveling through a tunnel or with data roaming turned off). The device still has cellular coverage for SMS. AWS falls back to SMS so that the message is delivered.
SMS text message from short code 47205 showing a Verizon Call Filter trial activation notice in a dark-themed mobile messaging app, with no sender branding, a generic profile icon, and a "Report Spam" warning at the bottom.

Figure 1: A typical SMS business message often appears from an unrecognizable short code, making it difficult for customers to verify the sender before clicking a link or replying.

When RCS delivery falls back to SMS because of a lack of data connectivity or other availability reasons, AWS uses sticky sending. The service prioritizes the origination number that most recently delivered successfully to that destination—maintaining that preference for 24 hours before retrying RCS. This ensures consistent, recognizable delivery across various connectivity and compatibility scenarios.

Effective phone number management is the foundation for fallback behavior. AWS provides three ways to send messages, each with different fallback behavior:

  • Pool-based sending — AWS selects from identities in a specific pool containing your RCS agent and SMS phone numbers. This is the recommended approach for production deployments. Pools give you precise control over which identities are used while AWS handles automatic routing and fallback.
  • Account-level sending — AWS automatically selects the best identity from your entire account. This is similar to the default behavior in Amazon Simple Notification Service (SNS), where you cannot isolate traffic into specific pools. This approach is ideal for development, testing, or simple deployments where a single identity is used for all messaging use cases within a country.
  • Direct send — You specify an exact RCS agent as the origination identity. The message fails if RCS isn’t available. Use this for testing or when you want to handle fallback yourself.

For production messaging where delivery is critical, use pool-based or account-level sending for reliable delivery. Pools route your fallback SMS messages through consistent, recognizable numbers your customers trust.

RCS vs. SMS at a glance

For a detailed comparison of capabilities, see the following table.

Feature SMS RCS
Character limit 160 characters No practical limit
Media support MMS (compressed) High-resolution images, video, audio
Read receipts No Yes
Typing indicators No Yes
Interactive buttons No Yes
Branded identity Basic (Sender ID) Full (Verified Profile)
Delivery over internet No Yes
Verified RCS business profile for "Go Big or Go Home!" showing a branded hippo logo, purple banner, company tagline, and contact options for call, website, and email in a mobile messaging app.

Figure 2: The final result – A verified brand profile featuring your high-resolution logo, banner image, and custom brand colors—elements that significantly increase trust and click-through rates compared to standard SMS.

The recommended adoption path

Consider a phased approach to RCS adoption that aligns with your operational readiness. First, register your brand and get carrier approval. Next, move your existing SMS use cases to RCS. Finally, after you are comfortable with the channel, test and expand with advanced use cases.

How to register

Brand asset requirements

Before submitting your registration, prepare the following brand assets. Carriers reject assets that don’t meet exact specifications, so verify these requirements before submitting.

Asset Requirements
Logo 224×224 pixels, PNG with transparency, under 50 KB
Banner 1440×448 pixels, PNG or JPEG, under 200 KB
Brand Color Hex format (e.g. #1A73E8), minimum 4.5:1 contrast ratio

Note: A 4.5:1 contrast ratio means your brand color must be at least 4.5 times brighter (or darker) than its background. This threshold meets WCAG 2.1 Level AA standards, ensuring your brand name is legible for users with moderate vision loss or color blindness. To verify compliance, use a Contrast Checker to test your hex code against a solid white background.

Use case selection

Your use case determines what types of messages you can send in production. Select carefully before submitting — the use case does not affect approval timeline, but it does determine your message restrictions and how carriers perceive your traffic.

Use Case What you can send
OTP Authentication codes and security verification only
Transactional Order updates, shipping notifications, account alerts
Promotional Marketing campaigns and offers (requires opt-in consent)
Multi-use Combined transactional and promotional messaging

Why not just choose Multi-use for everything?

While Multi-use offers the most flexibility, it is often subject to stricter carrier scrutiny during the vetting process. Carriers prefer single-purpose agents (like OTP) because they provide a more predictable and trustworthy experience for the recipient. If you have a high-volume OTP use case, registering it separately can help protect your sender reputation from being impacted by the lower engagement rates typically associated with promotional marketing.

Important: Agents must be use-case specific. Sending message types that don’t match your registered use case could result in suspension.

Registration steps

To submit your registration, complete the following steps:

  1. Sign in to the AWS Management Console and open the AWS End User Messaging console.
  2. In the navigation pane, under Configurations, choose RCS agents.
  3. Choose Create RCS Agent. This creates an AWS RCS Agent and then immediately guides you through creating a testing registration in a single workflow.
RCS tester invitation from RBM Tester Management showing interactive "Make me a tester" and "Decline" buttons, user selection, and confirmation message for the "Go Big or Go Home!" RCS agent in a dark-themed mobile messaging app.

Figure 3: Once your agent is created in the AWS console, your registered test devices will receive an invitation like this one. Tapping ‘Make me a tester’ allows you to immediately see your branded content in action.

  1. The next screen shows an introduction to RCS and explains the setup process. Review the information and choose Next to continue.
  2. On the Agent details page, set the following:
    1. Friendly name — A console-only label for your AWS RCS Agent. This is an internal name for your reference (stored as a tag) and is not the name displayed on recipients’ phones. The friendly name is not available through the API.
    2. Deletion protection — (Optional) Enable to prevent accidental deletion of the agent.
    3. Tags — (Optional) Add tags to organize and identify your agent.
  3. In the Brand information section of the same page, enter the following:
    1. Display name — The brand name that recipients see alongside your RCS messages.
    2. Description — A brief description of your brand or business.
    3. Use case — Select the primary use case for your RCS messaging (for example, transactional notifications, marketing, or customer support).
  4. In the Brand assets section of the same page, upload the following:
    1. Logo — 224 × 224 pixels, PNG with transparency, under 50 KB.
    2. Banner image — 1440 × 448 pixels, PNG or JPEG, under 200 KB.
    3. Brand color — A hex color code (for example, #1A73E8) with a minimum contrast ratio of 4.5:1 against a white background.

Important: Some brand assets cannot be changed after the agent is submitted for registration. Prepare your final brand assets before creating the agent. If you want to experiment first, you can quickly create a test agent using this flow, then create a fresh AWS RCS Agent with finalized brand assets later.

  1. On the Compliance keywords page, configure your keywords and auto-response messages.
  2. On the Review page, verify all your settings.
  3. Choose Validate and submit to create the AWS RCS Agent and submit the testing registration.

Testing and production launch phases

Launching RCS follows a distinct path from testing to production:

  1. Testing Registration: The initial guided console flow creates your AWS RCS Agent and a testing agent, or RBM Agent(RCS Business Messaging Agents). This allows you to validate your integration immediately by sending messages to registered test devices without waiting for carrier approval.
  2. Country Launch Registrations: After testing is complete, you must submit separate country launch registrations for each production market.

Carrier review and approval

  • Independent Approval: Each country launch registration undergoes a separate review process by every carrier in that target country.
  • Partial Reach: Approval is per-carrier. You are considered “partially approved” as soon as at least one carrier approves your agent, allowing you to start sending production messages to recipients on that carrier’s network via the SendTextMessage API.
  • Timelines: For both the U.S. and Canada, expect the carrier approval process to take several months. To avoid delays, verify that all registration fields are accurate and, for U.S. launches, provide a clear screen recording demonstrating your intended use case.

Important considerations

Multi-level identity: Think of the AWS RCS Agent as your brand’s unified identity. Under this one resource, you will have multiple RCS for Business IDs: one for your testing agent and separate IDs for each country launch (e.g., one for the US and one for Canada).

Carrier approval is per-carrier, not all-at-once: You do not need to wait for every carrier to approve before you begin sending. As soon as an individual carrier approves your agent, you can reach that carrier’s subscribers.

Sandbox testing: Testing with sandbox agents does not require carrier approval and can begin immediately upon submission. Note that testing messages are charged at standard RCS rates.

Finality of configurations: Brand assets are defined on each specific registration and are final after submission. While minor updates are permitted through supporting documentation, significant structural changes require creating a new agent. Plan your configuration carefully before you submit.

Accuracy matters: Filling out registration forms incorrectly can result in lengthy delays or rejection. Double-check all information before submitting and verify that business documents are current and valid. In this early phase of RCS adoption, carriers have been approving recognizable brands more readily.

Managing costs and usage

Monitor your RCS message volume through Amazon CloudWatch metrics and set up billing alerts to track spending against your SMS baseline. For more information, see AWS End User Messaging pricing.

Conclusion

In this post, we showed you how RCS on AWS End User Messaging solves customer engagement challenges through verified branding, interactive features, and automatic SMS fallback. You get a branded messaging experience with the reliability of SMS built in. Evaluate your current SMS message volume and identify high-priority transactional messages that would benefit from verified branding. Consider migrating these high-impact use cases first to establish your brand presence and improve customer trust.

Get started today

Ready to implement RCS? Here are your next steps:

  • If you’re ready to register: Contact your AWS account team or AWS Support to begin the registration process.
  • If you want to learn more: Review the AWS End User Messaging and RCS documentation.
  • If you’re still evaluating: Start by auditing your current SMS message volume and identifying high-priority transactional messages that would benefit from verified branding.

About the authors

Building a Scalable Messaging API with AWS End User Messaging and SES

Post Syndicated from Tyler Holmes original https://aws.amazon.com/blogs/messaging-and-targeting/building-a-scalable-messaging-api-with-aws-end-user-messaging-and-ses/

Modern applications often need to send notifications across multiple channels either through email and/or SMS. However, building a reliable messaging system that manages templates, handles failures gracefully, scales, and maintains security can be challenging. Following this guide, you’ll learn how to build a template manager and messaging API using API Gateway with JWT authentication for secure access, Amazon SQS for reliable message queuing, AWS Lambda for serverless processing, AWS End User Messaging for SMS, and Amazon Simple Email Service (SES) for email.

Architecture overview

You deploy a decoupled architecture that separates message ingestion from processing, providing resilience and scalability.

Fig. 1 Message Template Manager Architecture

Fig. 1 Message Template Manager Architecture

Architecture flow

  1. Client Application sends authenticated requests with JWT tokens
  2. API Gateway validates requests using a Lambda Authorizer
  3. Lambda Authorizer retrieves the JWT secret from AWS Secrets Manager and validates the token
  4. API Gateway sends validated messages to the SQS Queue
  5. Lambda Processor polls messages from SQS in batches
  6. Lambda Processor retrieves message templates from DynamoDB (if needed)
  7. Lambda Processor sends emails through Amazon SES and SMS through AWS End User Messaging
  8. Failed messages (after 3 retries) move to the Dead Letter Queue
  9. CloudWatch Alarm triggers when messages arrive in the DLQ

If a message fails processing after three attempts, it moves to a Dead Letter Queue (DLQ) where it’s preserved for 14 days, and a CloudWatch alarm notifies you of the failure.

Key features

With this architecture, you get several important capabilities:

  • JWT Authentication: Secure API access using JSON Web Tokens stored in AWS Secrets Manager
  • Automatic Retries: Failed messages retry up to three times before moving to the DLQ
  • Partial Batch Failures: Only failed messages retry, not the entire batch
  • Template Management: Store reusable message templates in Amazon DynamoDB
  • Multi-Channel Support: Send email through Amazon SES and SMS through AWS End User Messaging
  • Configuration Set Support: Track delivery metrics and route events with per-message or deployment-level configuration sets
  • Monitoring: CloudWatch alarms alert you when messages fail

Implementation details

1. API Gateway with JWT authorization

The API Gateway uses a Lambda authorizer to validate JWT tokens before allowing requests through:

def lambda_handler(event, context):
    token = event.get('authorizationToken', '').replace('Bearer ', '')
    try:
        # Retrieve secret from AWS Secrets Manager
        jwt_secret = get_jwt_secret()
        
        # Validate JWT token
        payload = jwt.decode(
            token,
            jwt_secret,
            algorithms=['HS256'],
            issuer='messaging-api'
        )
        
        # Generate IAM policy to allow request
        return generate_policy(payload.get('sub'), 'Allow', event['methodArn'])
    except jwt.ExpiredSignatureError:
        raise Exception('Unauthorized: Token expired')
    except jwt.InvalidTokenError:
        raise Exception('Unauthorized: Invalid token')

The JWT secret is stored securely in AWS Secrets Manager and cached in the Lambda execution environment for performance.

2. SQS queue configuration

The SAM template defines two queues with appropriate settings:

MessagesQueue:
  Type: AWS::SQS::Queue
  Properties:
    QueueName: MessagesQueue
    VisibilityTimeout: 300  # 5 minutes
    MessageRetentionPeriod: 345600  # 4 days
    RedrivePolicy:
      deadLetterTargetArn: !GetAtt MessagesDeadLetterQueue.Arn
      maxReceiveCount: 3

MessagesDeadLetterQueue:
  Type: AWS::SQS::Queue
  Properties:
    QueueName: MessagesDeadLetterQueue
    MessageRetentionPeriod: 1209600  # 14 days

The visibility timeout of 5 minutes prevents duplicate processing while giving the Lambda function enough time to complete. Messages that fail three times automatically move to the DLQ.

3. Lambda message processor

The Lambda function processes messages from SQS and sends them through the appropriate channel:

def lambda_handler(event, context):
    failed_messages = []
    
    for record in event['Records']:
        message_id = record['messageId']
        try:
            message = json.loads(record['body'])
            
            # Process email if configured
            if 'EmailMessage' in message:
                send_emails(message)
            
            # Process SMS if configured
            if 'SMSMessage' in message:
                send_sms_messages(message)
                
        except Exception as e:
            print(f"Error processing message {message_id}: {str(e)}")
            failed_messages.append({"itemIdentifier": message_id})
    
    # Return failed messages for automatic retry
    return {"batchItemFailures": failed_messages}

Configuration Sets for tracking and analytics:

Configuration Sets enable you to track delivery metrics, monitor costs, and route events to analytics pipelines. You can set defaults at deployment time and override them per-message:

  • Deployment-level defaults: Set SMSConfigurationSet parameter during deployment to apply to all messages
  • Per-message override: Include ConfigurationSetName in the SMSMessage payload to use different tracking for specific messages

This flexibility lets you separate analytics by message type, campaign, or priority without requiring redeployment.

4. Template management with DynamoDB

Message templates are stored in DynamoDB for reusability:

def get_email_template_from_dynamodb(template_name, substitutions):
    response = templates_table.get_item(Key={'TemplateName': template_name})
    
    if 'Item' not in response:
        return build_default_email(), "Account Alert"
    
    template_body = response['Item']['MessageBody']
    subject = response['Item'].get('Subject', 'Notification')
    
    # Replace {variable} placeholders with actual values
    rendered_body = replace_variables(template_body, substitutions)
    rendered_subject = replace_variables(subject, substitutions)
    
    return rendered_body, rendered_subject

You can update message content without redeploying code.

Template size considerations:

DynamoDB has a 400 KB limit per item, which includes all attribute names and values. For message templates, this means:

  • Typical email templates (5-20 KB) fit comfortably
  • SMS templates (< 1 KB) have no practical constraints

If you need to store templates larger than 400 KB, consider storing them in Amazon S3 and referencing the S3 object key in DynamoDB. This hybrid approach provides unlimited template size while maintaining fast lookups.

Prerequisites

Before deploying this solution, ensure you have the following:

  • AWS End User Messaging SMS configured with a phone pool or origination identity for SMS sending
  • Amazon SES configured with verified email identities (sender and recipient for sandbox mode)
  • IAM permissions to create Lambda functions, API Gateway, SQS queues, DynamoDB tables, and Secrets Manager secrets
  • Python 3.9 or later installed locally
  • AWS SAM CLI installed (version 1.0 or later)
  • AWS CLI installed and configured with your credentials
  • An active AWS account with appropriate permissions

Deployment

You use AWS SAM for infrastructure as code. Deploy with these commands:

sam build
sam deploy --guided

During deployment, you’ll set a JWT secret that’s stored in AWS Secrets Manager. Use a strong, random secret for production:

python -c "import secrets; print(secrets.token_urlsafe(32))"

Usage example

Once deployed, send messages by making authenticated API requests:

curl -X POST "https://your-api-endpoint/dev/" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -d '{
    "TraceId": "12345",
    "EmailMessage": {
      "FromAddress": "[email protected]",
      "Subject": "Low Balance Alert",
      "ConfigurationSetName": "email-analytics",
      "Substitutions": {
        "productName": "CHEQUING",
        "membershipNumber": "****5493",
        "accountBalance": "100.00"
      }
    },
    "SMSMessage": {
      "MessageType": "TRANSACTIONAL",
      "OriginationNumber": "your-pool-id",
      "TemplateName": "alert-template",
      "ConfigurationSetName": "sms-analytics"
    },
    "Addresses": {
      "[email protected]": {
        "ChannelType": "EMAIL"
      },
      "+16048621234": {
        "ChannelType": "SMS",
        "Substitutions": {
          "productName": "CHEQUING",
          "membershipNumber": "****7303",
          "accountBalance": "100.00"
        }
      }
    }
  }'

Using Configuration Sets:

The example above shows optional ConfigurationSetName parameters for both email and SMS. These enable:

  • Delivery tracking: Monitor delivery rates, failures, and bounce metrics
  • Cost monitoring: Track spending per campaign or message type
  • Event routing: Send delivery events to CloudWatch, Kinesis, or SNS for analytics
  • Segmented metrics: Separate analytics by use case, priority, or customer segment

If you don’t specify a ConfigurationSetName in the request, the system uses the deployment-level default (if configured).

Monitoring and operations

CloudWatch alarms

The solution includes a CloudWatch alarm that triggers when messages arrive in the DLQ:

DLQAlarm:
  Type: AWS::CloudWatch::Alarm
  Properties:
    AlarmName: !Sub ${AWS::StackName}-DLQ-Messages
    MetricName: ApproximateNumberOfMessagesVisible
    Namespace: AWS/SQS
    Statistic: Sum
    Period: 300
    EvaluationPeriods: 1
    Threshold: 1
    ComparisonOperator: GreaterThanOrEqualToThreshold

Handling failed messages

When messages fail, you can inspect them in the DLQ and redrive them back to the main queue after fixing the issue:

# Check DLQ depth
aws sqs get-queue-attributes \
  --queue-url YOUR_DLQ_URL \
  --attribute-names ApproximateNumberOfMessages

# Redrive messages from DLQ to main queue
aws sqs start-message-move-task \
  --source-arn YOUR_DLQ_ARN \
  --destination-arn YOUR_MAIN_QUEUE_ARN

Viewing logs

Lambda automatically logs to CloudWatch Logs:

aws logs tail /aws/lambda/MessageProcessor --follow

Cost considerations

For 1 million messages per month estimated costs are:

Service Usage Cost
API Gateway 1M requests $3.50
Amazon SQS 1M messages $0.40
AWS Lambda 1M invocations (128MB, 1s avg) $2.50
Amazon SES 1M emails $100.00
AWS End User Messaging SMS 1M SMS $ Varies based on destination

Note: The serverless architecture means you only pay for what you use, with no minimum fees or upfront costs.

Security best practices

This solution follows several security best practices:

  1. JWT Authentication: All API requests require valid JWT tokens
  2. Secrets Manager: JWT secrets are stored encrypted in AWS Secrets Manager
  3. IAM Least Privilege: Each Lambda function has only the permissions it needs
  4. HTTPS Only: API Gateway enforces HTTPS for all requests
  5. Token Caching: Authorization decisions are cached for 5 minutes to reduce latency

Clean up

To avoid incurring ongoing charges, delete the resources created by this solution when you no longer need them:

  1. If you configured AWS End User Messaging phone pools or origination identities for this solution, remove them from the End User Messaging console
  2. If you created Amazon SES email identities specifically for this solution, remove them from the SES console
  3. Verify that all resources (Lambda functions, API Gateway, SQS queues, DynamoDB table, Secrets Manager secret) have been removed in the AWS Management Console
  4. Delete the CloudFormation stack by running: sam delete --stack-name <your-stack-name>

Conclusion

With this architecture, you can build a production-ready messaging API using AWS serverless services. The decoupled design gives you resilience through automatic retries and dead letter queues, while the serverless approach eliminates infrastructure management and scales automatically.

The complete solution is deployable through AWS SAM and includes:

  • JWT authentication with AWS Secrets Manager
  • Multi-channel messaging (email and SMS)
  • Template management with DynamoDB
  • Configuration Set support for tracking and analytics
  • Comprehensive monitoring and alerting
  • Automatic retry and failure handling

You can extend this architecture by adding more channels (push notifications, webhooks), implementing message scheduling, or integrating with Amazon EventBridge for event-driven workflows.

Additional resources

The complete source code for this solution is available in the accompanying GitHub repository, including SAM templates, Lambda functions, and deployment scripts.


About the authors

Adding a voice layer to WhatsApp conversations with AWS End User Messaging

Post Syndicated from Pavlos Ioannou Katidis original https://aws.amazon.com/blogs/messaging-and-targeting/adding-a-voice-layer-to-whatsapp-conversations-with-aws-end-user-messaging/

Businesses around the world use WhatsApp as a primary channel to connect with customers. It’s familiar, trusted, and effective for everything from booking confirmations to customer support. But most of these conversations are still text-only. For many customers, text is fast and efficient. Yet there are times when typing is inconvenient, slow, or less effective at conveying nuance. In those moments, voice messages can transform the interaction — making it faster, more inclusive, and more human.

With AWS End User Messaging, businesses can now enable both voice note input and voice note responses on WhatsApp. Customers send a voice note, and a bot can respond with a natural-sounding voice note reply. Note: This solution processes asynchronous voice notes (recorded audio messages), not real-time voice calls. In this blog post, we explore why voice notes matter, where they make a difference, and how AWS helps you enable them through a sample voice note messaging solution.

Watch an end to end demo here.

Why voice notes matter in customer messaging

Text remains essential, but research shows that voice notes adds unique advantages:

  • Richer communication: Voice carries tone, urgency, and emotion — reducing misunderstandings and helping businesses respond more appropriately (Preply survey).
  • Natural and fast: Speaking is up to three times faster than typing on mobile devices, especially when users are on the go (Sherry Ruan, Jacob O. Wobbrock, Kenny Liou, Andrew Ng, and James A. Landay. 2018. Comparing Speech and Keyboard Text Entry for Short Messages in Two Languages on Touchscreen Phones. Proc. ACM Interact. Mob. Wearable Ubiquitous Technol. 1, 4, Article 159 (December 2017), 23 pages. https://doi.org/10.1145/3161187).
  • Accessibility and inclusivity: Voice lowers barriers for people with limited literacy or visual impairments. Elderly customers or those with difficulty reading long text messages benefit significantly.
  • Context-driven preference: A YouGov study across 17 markets found that while text is still preferred overall, a notable share of users choose both text and audio depending on situation (YouGov survey).

Where voice notes make a difference

Voice messaging is especially useful when speaking feels more natural than typing—helping customers communicate in ways that fit their situation and needs.

  • Elderly customers – easier to listen than to read.
  • Field workers or drivers – easier to speak than to type while working.
  • Healthcare – patients can describe symptoms naturally by voice.
  • Hospitality and reservations – “Book a table for 7 pm” is faster to say than to navigate online calendar.
  • Customer support escalation – complex issues are often resolved more quickly with a voice exchange.

Voice notes don’t replace text. It complements it — giving customers the flexibility to communicate in the way that best suits their context.

AWS End User Messaging and WhatsApp

AWS End User Messaging is a managed AWS service that enables businesses to send and receive messages across multiple channels, including WhatsApp, SMS, MMS (US only), outbound voice, and push notifications.

When you use AWS End User Messaging for WhatsApp, you benefit from AWS’s global scale, resilience, and security. Inbound WhatsApp messages are automatically published to an Amazon SNS topic, enabling the integration with other AWS services such as Amazon SQS queues, AWS Lambda functions or Amazon Bedrock for downstream processing.

This flexibility is also what makes voice-to-voice messaging possible. Businesses can process inbound voice messages with Lambda, apply speech-to-text and text-to-speech services like Amazon Transcribe and Amazon Polly, or integrate third-party models such as Whisper through the AWS Marketplace for Amazon Bedrock.

Voice notes messaging solution

To demonstrate how voice can be enabled on WhatsApp, check out the AWS CDK sample project:  GitHub – WhatsApp Voice Notes Messaging

The solution shows how to:

  • Receive a WhatsApp voice note through AWS End User Messaging.
  • Transcribe the voice input to text.
  • Process it with conversational bot logic.
  • Convert the response back into a natural-sounding voice note.
  • Send the reply to the user on WhatsApp.

You can enable inbound only, outbound only, or a full voice-to-voice  notes loop depending on your requirements.

Getting started

The complete solution is available as an open-source AWS CDK project. To get started, you’ll need:

Implementation

Clone the repository and deploy the solution:

git clone https://github.com/aws-samples/sample-whatsapp-voice-to-voice-messaging
cd sample-whatsapp-voice-to-voice-messaging
npm install

Before deploying, you’ll need to configure your WhatsApp phone number ID in the CDK context or parameters. The deployment will prompt you for this configuration, or you can set it in the cdk.json file. Once configured, deploy with:

cdk deploy

The CDK stack automatically provisions all required AWS resources including Lambda functions, SNS topics, S3 buckets, and IAM roles.

Clean up

To remove all resources and avoid ongoing charges:

cdk destroy

For detailed architecture diagrams, configuration options, and step-by-step setup instructions, visit the GitHub repository.

Conclusion

Customers are already using voice notes in their personal WhatsApp conversations. Bringing that same option into business communication makes customer interactions more natural, inclusive, and efficient.

With AWS End User Messaging and its WhatsApp channel, you can add voice alongside text without changing how customers connect to you. And with the sample CDK project, you can try it out today, experiment, and extend it for your own business needs.

Explore the project here: AWS Sample – WhatsApp Voice Notes Messaging


About the authors

How to improve email sender reputation with Amazon SES Email Validation

Post Syndicated from Zip Zieper original https://aws.amazon.com/blogs/messaging-and-targeting/how-to-improve-email-sender-reputation-with-amazon-ses-email-validation/

If you’re sending emails at scale with Amazon Simple Email Service (Amazon SES), maintaining high deliverability depends on more than the content you send. It’s about who receives those emails. Mailbox providers like Gmail, Yahoo, and Outlook assign reputation scores based on your sending practices, domain and IP authentication records, message quality, and recipient engagement. These providers use their own algorithms to decide whether your emails reach the inbox, are filtered as spam, or aren’t delivered at all. For more information about managing your email reputation, see The Four Pillars of Managing Email Reputation. In this post, we show you how the Amazon SES Email Validation feature can help you to protect your sender reputation.

The email bounce rate is the percentage of emails that fail to deliver and is one of the most critical factors affecting your sender reputation. Every bounce damages your sender reputation. Mailbox providers like Gmail and Outlook closely monitor bounce rates, and accounts that bounce over 5% trigger warnings. If your account bounce rate exceeds 10%, the email services providers might throttle, or completely block sending. For customers sending email at scale with Amazon SES, a high bounce rate may trigger immediate consequences: damaged sender reputation, blocked deliverability, and ISP penalties that can throttle or suspend your entire email program. Traditional approaches to email quality are reactive, because you will only discover problems after bounces have damaged your reputation. While account suppression lists protect against known problematic addresses, they can’t protect you from the normal decay of email address quality that occurs because of job changes, abandoned mailboxes, domain expirations, or bots and bad actors looking to damage your email reputation.

Use Amazon SES Email Validation to help you protect your sender reputation

Amazon SES Email Validation shifts bounce management from reactive to proactive, helping you detect problems before they damage your sender reputation. The feature provides two validation approaches: the Email Validation API for timely checks during registration and Auto Validation to automatically review all outbound email addresses before sending and only deliver messages to recipients that meet your selected validation threshold. Both methods are intended to catch problem addresses before they become bounces, helping to protect your sender reputation.

In this post, we guide you through implementing both validation approaches using AnyCompany—a fictitious ecommerce website—as our example. You will see how AnyCompany might use the Email Validation API for timely registration checks on address acquisition and Auto Validation at time of sending. You’ll learn how to protect your sender reputation proactively and integrate validation into existing workflows with minimal disruption. We also show you how to use Amazon CloudWatch metrics to improve email list health over time. After you’re done reading and experimenting, you’ll understand how Amazon SES Email Validation can help transform your email operations from reactive bounce management to proactive quality assurance.

Solution overview – how to use the Email Validation API to avoid ingesting invalid email addresses

You can use the Email Validation API to validate email addresses through synchronous API calls to check addresses at the point of collection. This method gives you immediate feedback about address validity and helps prevent invalid addresses from entering your database. You control when validation occurs and how to handle the results. The Email Validation API costs $0.01 per validation using the API or the AWS Management Console for Amazon SES. See Amazon SES pricing for details.

The Amazon SES console uses the Email Validation API to manually validate up to 10 email addresses at a time. The results are shown in the console—shown in the following screenshot—and you can export the results to a CSV file.

The Email Validate API can be used in your code or using the AWS Command Line Interface (AWS CLI) to validate individual email addresses through synchronous API calls. This method is well-suited for validating addresses at the point of collection—during user registration, subscription form submission, or during an email list import to help prevent invalid addresses from entering your database. The following is an example using the AWS CLI.

aws sesv2 get-email-address-insights \
    --email-address [email protected] \
    --region us-east-1

The API returns a response structure similar to the following example:

{
  "MailboxValidation": {
    "IsValid": {
      "ConfidenceVerdict": "HIGH"
    },
    "Evaluations": {
      "HasValidSyntax": {
        "ConfidenceVerdict": "HIGH"
      },
      "HasValidDnsRecords": {
        "ConfidenceVerdict": "MEDIUM"
      },
      "MailboxExists": {
        "ConfidenceVerdict": "MEDIUM"
      },
      "IsRoleAddress": {
        "ConfidenceVerdict": "LOW"
      },
      "IsDisposable": {
        "ConfidenceVerdict": "LOW"
      },
      "IsRandomInput": {
        "ConfidenceVerdict": "LOW"
      }
    }
  }
}

Understanding Email Validation API verdicts

For each email, the Email Validation API returns an overall validity confidence with three possible aggregate verdicts:

  • HIGH – The email address passed all critical validation checks and is highly likely to be deliverable. These addresses can be accepted without additional scrutiny.
  • MEDIUM – The email address passed basic validation but has characteristics that might affect deliverability (such as being a role address or having uncertain mailbox existence). Your use case and bounce risk tolerance should be used to determine whether to accept these addresses.
  • LOW – The email address failed one or more critical validation checks and is unlikely to be deliverable. Your use case and bounce risk tolerance will most likely cause you to reject these addresses.

To reach the overall validity confidence, the Email Validation API performs six detailed checks on each email address:

  • Syntax validation (HasValidSyntax) – Confirms the address follows RFC 5321 and RFC 5322 standards for email address formatting. This catches obvious errors such as missing @ symbols or invalid characters.
  • DNS verification (HasValidDnsRecords) – Validates that the domain exists and has proper mail exchange (MX) records and corresponding A records configured. This helps confirm that the domain can receive email.
  • Mailbox existence (MailboxExists) – Predicts whether the specific mailbox exists and can receive messages.
  • Role address detection (IsRoleAddress) – Identifies generic addresses like [email protected] or [email protected] that typically represent shared mailboxes rather than individual recipients.
  • Disposable email detection (IsDisposable) – Checks temporary email services like mailinator.com or guerrillamail.com that users often employ to avoid providing real contact information.
  • Random input detection (IsRandomInput) – Checks randomly generated patterns.

For more information about response values and data types, see the MailboxValidation data type in the Amazon SES API v2 reference.

The Email Validation API provides a dashboard in the Amazon SES console that you can use to view email address verification results over time, with the ability to look back for up to one month, as shown in the following screenshot.

Use auto validation to help prevent bounces when sending from Amazon SES

Amazon SES Auto Validation automatically performs comprehensive address validation through multiple checks such as syntax validation, DNS records, and others before each message is sent. When auto validation is enabled, Amazon SES will only deliver messages to recipients that meet your selected validation threshold. This helps you protect your sender reputation by preventing sends to addresses that have a high probability of being invalid or risky without requiring manual intervention or API integration. Auto Validation must be enabled separately for each AWS region in your account. For example, if you enable it in us-east-1, it will not be active in us-west-2 unless you explicitly enable it there as well. You can enable it at the account level for an entire region, or selectively within configuration sets. Auto validation costs $0.01 per 1,000 validations. Be aware that sends suppressed by Auto Validation count towards your daily send quota, and you will be charged the standard outgoing message fee for suppressed sends (in addition to the fee for auto validation). See Amazon SES pricing for more information.

When enabled at the AWS account level, you set the Validation threshold to determine which email addresses to suppress based on their validity confidence, as shown in the following screenshot.

  • Amazon SES managed threshold (recommended) – Amazon SES automatically manages the threshold to suppress invalid addresses based on your sending patterns and reputation. This option allows Amazon SES to optimize the validation threshold dynamically. Use this threshold when you want AWS to handle validation decisions based on your account’s specific characteristics.
  • Custom threshold –
    • High – Delivers emails only to addresses with high delivery likelihood. This provides maximum protection for your sender reputation but might suppress some legitimate addresses with medium delivery confidence. Use this threshold for critical transactional emails or when protecting sender reputation is your top priority.
    • Medium – Delivers emails to addresses with medium or high delivery likelihood. This balances reputation protection with delivery reach by allowing addresses with moderate deliverability scores. Use this threshold for marketing campaigns where you want to maximize reach while still filtering obviously invalid addresses.

You will usually find that using the recommended Amazon SES managed threshold works best for the bulk of your sending, however for certain use cases you might want to override the account setting and use a custom threshold in your configuration set. If you choose High or Medium thresholds instead of Amazon SES managed (as shown in the following screenshot), it’s important that you monitor your delivery metrics and validation results regularly.

Auto Validation applies to all outbound emails sent through your account. Addresses that don’t meet your threshold will be suppressed with the bounceSubType of EmailValidationSuppressed. Suppressed sends count towards your daily send quota, and you will be charged the standard outgoing message fee for suppressed sends in addition to the fee for auto validation.

{
  "Type": "Notification",
  "MessageId": "0ded6fd6-4e59-5ae0-9782-0e68faa886e7",
  "TopicArn": "arn:aws:sns:us-east-1:252640393490:ses-auto-validate",
  "Subject": "Amazon SES Email Event Notification",
  "Message": "{\"**eventType**\":\"**Bounce**\",\"bounce\":{\"feedbackId\":\"0100019b345a05a0-95e3062a-9594-499f-aafc-dc2dc9647cb6-000000\",\"**bounceType**\":\"**Permanent**\",\"**bounceSubType**\":\"**EmailValidationSuppressed**\",\"bouncedRecipients\":"
}

How AnyCompany might use the Email Validation API and auto validation

AnyCompany runs an ecommerce platform for both business and consumer office supplies. The company’s website hosts various web-forms for customers to create accounts and sign up to receive newsletters and discount offers. When they place orders through the platform, AnyCompany’s system sends order confirmations and delivery tracking emails. Today, when a new user registers through one of the web forms, AnyCompany sends a verification email to confirm the user’s email address, contact details, and opt-in to the company’s emails. If a user misspells their email address, they will never receive this verification email. Frustrated, they might move on to another provider. Similarly, if a bot or bad actor deliberately submits invalid addresses to the web form, verification emails will bounce. Both scenarios cost AnyCompany money with no return; at scale, a high bounce rate might cause email service providers to throttle or block future sends. AnyCompany previously investigated various third-party email validation services, but the engineering work, security reviews, and costs outweighed the expected benefits. This necessitated the cloud team’s constant and careful vigilance over the company’s bounce rate and reputation, diverting resources that the company would prefer to deploy elsewhere. As an ecommerce company, AnyCompany needs to be highly protective of its sender reputation. With email validation now built directly into Amazon SES, AnyCompany can bypass the complexity and cost of third-party tools and directly benefit from proactive bounce prevention across all email use cases. In the following section, we guide you through the simple steps AnyCompany might take to implement Amazon SES Email Validation.

Prerequisites

Before implementing Email Validation, you’ll need:

AWS account :

  • An AWS account with Amazon SES enabled in your desired AWS Region
  • AWS CLI version 2.0 or later installed and configured

Required IAM permissions:

Your IAM user or role needs the following permissions to configure Email Validation:

  • ses:PutAccountSuppressionAttributes – To enable and configure Email Validation at the account level
  • ses:GetAccount to verify Email Validation configuration
  • ses:CreateConfigurationSet when creating a new configuration set
  • ses:PutConfigurationSetSuppressionOptionsto override validation settings for specific configuration sets
  • ses:GetEmailAddressInsights to call the Email Validation API
  • iam:CreateServiceLinkedRole  creates an IAM service-linked role that is used by Amazon SES to publish CloudWatch metrics
  • cloudwatch:GetMetricStatistics – To retrieve validation metrics

Development environment:

  • Familiarity with AWS CLI commands and JSON configuration files
  • For API integration, SDK support for Amazon SES API v2 in your preferred programming language
  • Access to your application’s user registration code

Existing Amazon SES configuration:

  • At least one verified email address or domain in Amazon SES

While the Email Validation API works with an AWS account that is in the Amazon SES sandbox, auto validation is best demonstrated after your AWS account has been granted production access.

  • (Optional) Configuration sets created for different email types (transactional, marketing, and so on)

Validating email addresses at acquisition with the Email Validation API

To prevent bad addresses from entering their customer database at time of acquisition, AnyCompany will integrate the Email Validation API directly into their registration form. When a user submits their contact details, including email address, the Email Validation API is used. Results arrive within 100 milliseconds, with the overall validity confidence and six detailed checks of the email address. AnyCompany will then use the results and custom business logic they designed for their different use cases. For example, for their B2B business, they might allow registrations with high overall confidences and a role address, such as [email protected]. For the consumer business, they might accept registrations with medium overall confidences, but always reject emails that the API identifies as disposable, such as [email protected] or random, such as [email protected].

Sample code snippets

The code snippets in this section are examples only and are not intended for production use.

Step 1: Validate email address on form submission

When a user submits the registration form, AnyCompany’s application calls the Email Validation API before creating the account:

import boto3

ses_client = boto3.client('sesv2', region_name='us-east-1')

def validate_registration_email(email_address):
    try:
        response = ses_client.get_email_address_insights(
            EmailAddress=email_address
        )
        return response
    except Exception as e:
        # Handle API errors gracefully
        print(f"Validation error: {e}")
        return None

Step 2: Apply business rules based on verdict

AnyCompany’s business logic handles different validation outcomes:

def should_accept_email(validation_response, registration_type):
    if not validation_response:
        # API error - accept email but flag for manual review
        return True, "accepted_with_warning"

    overall_verdict = validation_response['MailboxValidation']['IsValid']['ConfidenceVerdict']
    checks = validation_response['MailboxValidation']['Evaluations']

    # Always reject FAIL verdicts
    if overall_verdict == 'LOW':
        return False, "rejected_invalid"

    # Always accept PASS verdicts
    if overall_verdict == 'HIGH':
        return True, "accepted"

    # Handle NEUTRAL verdicts based on registration type
    if overall_verdict == 'MEDIUM':
        # Reject disposable emails for all registration types
        if checks['IsDisposable']['ConfidenceVerdict'] == 'HIGH':
            return False, "rejected_disposable"

        # Accept role addresses for B2B, reject for consumer
        if checks['IsRoleAddress']['ConfidenceVerdict'] == 'HIGH':
            if registration_type == 'b2b':
                return True, "accepted_role_address"
            else:
                return False, "rejected_role_address"

        # Accept other NEUTRAL cases with warning
        return True, "accepted_with_warning"

Step 3: Provide user-friendly error messages

When validation fails, AnyCompany provides specific, actionable feedback (you can add more conditions based on your requirements):

def get_user_error_message(validation_response):
    checks = validation_response['Evaluations']

    if checks['HasValidSyntax']['ConfidenceVerdict'] == 'LOW':
        return "Please check your email address for typos. It appears to have formatting errors."

    if checks['HasValidDnsRecords']['ConfidenceVerdict'] == 'LOW':
        return "The domain in your email address doesn't appear to exist. Please verify you entered it correctly."

    if checks['IsDisposable']['ConfidenceVerdict'] == 'HIGH':
        return "Temporary email addresses are not accepted. Please use a different email address."

    if checks['IsRoleAddress']['ConfidenceVerdict'] == 'HIGH':
        return "Please use a personal email address rather than a shared mailbox like support@ or admin@."

    return "We couldn't verify this email address. Please check for typos and try again."

Step 4: Suggest corrections for common mistakes

For addresses that fail DNS validation, AnyCompany suggests common corrections to popular domain typos.

def suggest_email_correction(email_address):
    common_domains = {
        'gmial.com': 'gmail.com',
        'gmai.com': 'gmail.com',
        'yahooo.com': 'yahoo.com',
        'hotmial.com': 'hotmail.com',
        'outlok.com': 'outlook.com'
    }

    # Extract domain from email
    if '@' in email_address:
        local, domain = email_address.split('@', 1)

        # Check for common misspellings
        if domain.lower() in common_domains:
            suggested_domain = common_domains[domain.lower()]
            return f"{local}@{suggested_domain}"

    return None

Key benefits of the Email Validation API

The Email Validation API provide proactive quality control by preventing invalid addresses from entering AnyCompany’s database, preventing the reputation damage that occurs when they send to addresses that bounce.

  • Immediate user feedback – Because validation results return within milliseconds, AnyCompany can provide real-time feedback during registration without impacting user experience.
  • Flexible policy enforcement – AnyCompany can use individual check results to define custom validation policies that match their various business requirements, accepting or rejecting addresses based on use case-specific risk tolerance.
  • Cost-effective validation – AnyCompany pays only for the addresses they validate, with no infrastructure to provision or manage and no license fees. Preventing a single bounce might save more than the cost of validation.

By integrating the Email Validation API into their registration workflow, AnyCompany can transform their approach from reactive bounce management to proactive quality assurance. Invalid addresses are prevented from entering their database, legitimate customers receive verification emails reliably, and their sender reputation remains protected with little to no ongoing effort.

Set up a CloudWatch alarm for high rates of LOW verdicts

You can configure CloudWatch alarms to notify you when validation patterns indicate a consistently high rate of LOW verdicts. This might indicate malicious bots attempting to sign up through a web-form or other mechanism.

The following example creates a CloudWatch alarm that fires when the rate of LOW verdicts exceeds 20%.

aws cloudwatch put-metric-alarm \
  --region us-east-1 \
  --alarm-name "EmailInsights-LOW-Rate-Above-20-Percent" \
  --alarm-description "Alarm when LOW confidence verdict rate exceeds 20%" \
  --comparison-operator GreaterThanThreshold \
  --threshold 20 \
  --evaluation-periods 2 \
  --treat-missing-data notBreaching \
  --metrics '[
    {
      "Id": "low",
      "MetricStat": {
        "Metric": {
          "MetricName": "EmailAddressInsights.ConfidenceVerdict.LOW",
          "Namespace": "AWS/SES"
        },
        "Period": 300,
        "Stat": "Sum"
      },
      "ReturnData": false
    },
    {
      "Id": "medium",
      "MetricStat": {
        "Metric": {
          "MetricName": "EmailAddressInsights.ConfidenceVerdict.MEDIUM",
          "Namespace": "AWS/SES"
        },
        "Period": 300,
        "Stat": "Sum"
      },
      "ReturnData": false
    },
    {
      "Id": "high",
      "MetricStat": {
        "Metric": {
          "MetricName": "EmailAddressInsights.ConfidenceVerdict.HIGH",
          "Namespace": "AWS/SES"
        },
        "Period": 300,
        "Stat": "Sum"
      },
      "ReturnData": false
    },
    {
      "Id": "e1",
      "Expression": "IF((low+medium+high)>0, low/(low+medium+high)*100, 0)",
      "Label": "LOW Rate Percentage",
      "ReturnData": true
    }
  ]'

How AnyCompany uses validation metrics

AnyCompany monitors their Email Validation dashboard in the Amazon SES console to track list quality trends. For example, if they notice an increase in disposable email failures, they can add additional client-side validation to their registration forms to discourage this behavior. When Auto Validation blocks a spike of invalid addresses from a specific partner marketing campaign, they avoid the problems associated with a spike in bounces while being better informed when investigating the list source and removing or cleaning it for future campaigns.

Validating email addresses at send time with Auto Validation

AnyCompany has been operating its online platform for many years without a way to validate email addresses. The company also makes frequent acquisitions and partnerships that regularly introduce new email addresses into their sending. This means that no matter how well the new registration for with the Email Validation API performs, they will always have some invalid email addresses in their outbound sends.

This is one of the scenarios that can be addressed with no code or process changes by using Auto Validation. When enabled at the AWS account level, Auto Validation checks each address before sending, automatically suppressing the send of invalid addresses, adding those addresses to the account suppression list, and generating bounce notification events. These bounce events appear in Amazon SES event publishing and can be monitored using Amazon CloudWatch, Amazon Simple Notification Service (Amazon SNS), or Amazon EventBridge or written to an Amazon Simple Storage Service (Amazon S3) bucket. Auto Validation bounce events appear as:

  • Bounce type: Permanent for addresses that will never be deliverable
  • Bounce subtypeEmailValidationSuppressed indicating Auto Validation blocked the send

Because it’s implemented in Amazon SES events, AnyCompany can handle address validation failures the same way they currently handle actual bounces from mailbox providers, maintaining consistency in their email processing workflows.

Conclusion

Amazon SES Email Validation addresses critical needs for organizations sending email at scale: preventing invalid addresses at registration and automatically filtering risky recipients before sending. The feature’s two complementary approaches—the Email Validation API for real-time checks and Auto Validation for automatic send-time filtering—give you flexibility to implement validation where it makes the most sense for your workflows.

Use the Email Validation API to:

  • Validate at point of collection (registration, imports)
  • Receive immediate user feedback
  • Build custom validation workflows
  • Validate before database entry
  • Validate up to 10 addresses

Use Auto Validation to:

  • Automatically protect ongoing campaigns automatically
  • Avoid code changes to sending logic
  • Provide consistent quality across all sends
  • Set organization-wide quality standards

By implementing both features of Amazon SES Email Validation, you can better protect your sender reputation by proactively preventing bounces, reducing the possibility of high bounce rates that can damage your deliverability.

Next steps

Start improving your email deliverability today:

  1. Enable Email Validation in your AWS account using the Amazon SES console or the AWS CLI
  2. Implement API validation at your registration points to improve data quality from the start
  3. Configure Auto Validation policies to protect your sender reputation across all campaigns
  4. Set up CloudWatch dashboards to track validation performance and identify list quality trends
  5. Review validation metrics weekly to refine your validation policies based on actual patterns

For more information about Amazon SES Email Validation, see the Amazon SES Developer Guide.


About the authors

Automate sender ID registration in AWS End User Messaging

Post Syndicated from Sarath Kumar Kallayil Sreedharan original https://aws.amazon.com/blogs/messaging-and-targeting/automate-sender-id-registration-in-aws-end-user-messaging/

AWS End User Messaging makes it possible to send SMS, MMS, push notifications, WhatsApp messages, and text to voice globally. When you send SMS, MMS, and voice messages with AWS End User Messaging, you must use a specific origination identity that supports sending these messages. Messaging options vary by country and include toll-free numbers (TFN), 10-digit long codes (US 10DLC), long codes, short codes, and sender IDs. To check a country’s available options, refer to Supported countries and regions for SMS messaging with AWS End User Messaging SMS.

This post explains how to programmatically register sender IDs, which can be used in many countries around the globe. The registration process makes it possible for businesses and organizations to send messages using an alphanumeric identifier instead of a phone number, making communications more professional and recognizable to recipients. For example, a fictitious company Example Corp could use the sender ID EXAMPLECO to send SMS. To learn more about sender ID registration, refer to Sender IDs in AWS End User Messaging SMS.

This post explores the AWS End User Messaging APIs required for programmatically registering sender IDs for the Indonesia and India. These sample scripts serve as a reference for sender ID registration in other countries. This automation approach simplifies the registration process, saving time and effort for businesses using AWS End User Messaging for their communication needs.

AWS End User Messaging APIs for sender ID registration

The AWS End User Messaging V2 API contains a set of actions that focus on sender ID registration management:

  • DescribeRegistrationTypeDefinitions – Retrieves registration type details for different countries. You can use DescribeRegistrationFieldDefinitions to view the requirements for creating, filling out, and submitting each registration type.
  • CreateRegistration – Creates a new registration. The RegistrationType field controls whether this is a registration for toll-free, 10DLC, or sender ID. This post will use a sender ID.
  • DescribeRegistrationFieldDefinitions – Retrieves field requirements for a specific registration type (retrieved by DescribeRegistrationTypeDefinitions).
  • PutRegistrationFieldValue – This action must be repeated for all required fields (retrieved by DescribeRegistrationFieldDefinitions).
  • CreateRegistrationAttachment – Uploads a required attachment (for example, a letter of authorization) for registration based on country-specific requirements.
  • SubmitRegistrationVersion – Submits the specified registration for review and approval. Make sure to verify all data is accurate before submitting the registration. The review process consists of the following steps:
    • After your script submits the registration, the initial status appears as CREATED and typically changes to REVIEWING within 24 hours.
    • After submission, your sender ID registration can’t be modified or deleted until the third-party registrar completes their review process.
    • If the status remains CREATED for over 24 hours after you’ve submitted your registration, open a support case for assistance.

Available actions for sender ID registration

As you manage your messaging campaigns in AWS End User Messaging SMS, several APIs are available to help you handle sender ID registrations efficiently:

  • CreateRegistrationVersion – Creates a new version of the registration and increases the VersionNumber. The previous version of the registration becomes read-only. This is useful for updating registration information while maintaining historical records.
  • DescribeRegistrationAttachments – Retrieves the specified registration attachments or all registration attachments associated with your AWS account. This helps in managing and reviewing documents linked to your registrations.
  • DescribeRegistrationFieldValues – Retrieves the specified registration field values. You can use this API to review current registration details for a specific version.
  • DescribeRegistrations – Retrieves the specified registrations and provides an overview of all your sender ID registrations, which is useful for multi-campaign management.
  • DescribeRegistrationSectionDefinitions – Retrieves the specified registration section definitions. You can use DescribeRegistrationSectionDefinitions to view the requirements for creating, filling out, and submitting each registration type. This API helps you understand the structure and requirements of different registration sections.
  • DescribeRegistrationVersions – Retrieves the specified registration version. You can use this API to track changes and view historical versions of a registration.

The following are important considerations for registration:

  • Most registration submissions undergo review by an independent third-party organization. This is a standard industry practice across SMS providers.
  • AWS does not review your registrations. It is important to complete this registration process with the understanding that AWS merely facilitates the submission process. AWS does not participate in or influence the third-party review process.
  • Each country has its own review process and timeline. Each registration is examined on a first-in/first-out basis by the registrar for each country. The registration review is conducted by external personnel unfamiliar with your company or use case. Therefore, it is crucial to provide clear and concise responses in your application.
  • After submitting your registration, the status begins as CREATED and typically transitions to REVIEWING within 24 hours
  • If AWS is able to provide you with a sender ID, AWS sends you an estimated time frame required for its provisioning. In many countries, AWS can provide you with a sender ID within 2–4 weeks. However, in some countries, it can take several weeks to obtain a sender ID.

AWS End User Messaging API usage flow for sender ID registration

The API flow consists of the following steps:

  1. Call DescribeRegistrationTypeDefinitions to understand available registration types.
  2. Use CreateRegistration to initiate the registration process.
  3. To obtain details about the required fields, call DescribeRegistrationFieldDefinitions.
  4. Use PutRegistrationFieldValue multiple times to populate all required fields.
  5. If needed, use CreateRegistrationAttachment to upload supporting documents.
  6. Finally, call SubmitRegistrationVersion to submit the registration for review.
  7. Use DescribeRegistrations periodically to check the status of the registration.

This API flow enables a fully automated sender ID registration process for supported countries. Businesses can efficiently manage registrations across various regulatory environments and geographical locations.

Registration field format

The AWS End User Messaging API uses a specific format to define the registration fields:

  • SectionPath – Represents the hierarchical location of a field within the registration form’s structure. For example, "SectionPath": "companyInfo".
  • FieldPath – The complete path to a specific field, combining the SectionPath with the field name. For example, "FieldPath": "companyInfo.companyName".
  • FieldType – Specifies the data type of the field (such as TEXT, SELECT, or ATTACHMENT). For example, "FieldType": "TEXT".
  • FieldRequirement – Indicates whether the field is REQUIRED, OPTIONAL, or CONDITIONAL. For example, "FieldRequirement": "REQUIRED".

Understanding these attributes is crucial for effective API interaction during the registration process. They define both the structure of your API calls and the necessary data inputs.

The following code is the subset of the response of DescribeRegistrationFieldDefinitions for the United Kingdom registration type (RegistrationType):

{
            "SectionPath": "companyInfo",
            "FieldPath": "companyInfo.companyName",
            "FieldType": "TEXT",
            "FieldRequirement": "REQUIRED",
            "TextValidation": {
                "MinLength": 1,
                "MaxLength": 100,
                "Pattern": "^(?=\\s*\\S)[\\s\\S]+$"
            },
            "DisplayHints": {
                "Title": "Company name",
                "ShortDescription": "Legal name which your company is registered under.",
                "ExampleTextValue": "Example Corp"
            }
        }
        
     {
            "SectionPath": "senderIdInfo",
"FieldPath": "senderIdInfo.senderIdDescription",
            "FieldType": "TEXT",
            "FieldRequirement": "OPTIONAL",
            "TextValidation": {
                "MinLength": 1,
                "MaxLength": 500,
                "Pattern": "^(?=\\s*\\S)[\\s\\S]+$"
            },
            "DisplayHints": {
                "Title": "Sender ID description",
                "ShortDescription": "If it is not obvious, explain the connection between your company name and this sender ID."
            }
        }
        
        {
            "SectionPath": "senderIdInfo",
            "FieldPath": "senderIdInfo.letterOfAuthorization",
            "FieldType": "ATTACHMENT",
            "FieldRequirement": "CONDITIONAL",
            "DisplayHints": {
                "Title": "Letter of authorization image",
                "ShortDescription": "Image of your signed letter of authorization (LOA)"
            }
        }
        {
            "SectionPath": "messagingUseCase",
            "FieldPath": "messagingUseCase.monthlyMessageVolume",
            "FieldType": "SELECT",
            "FieldRequirement": "REQUIRED",
            "SelectValidation": {
                "MinChoices": 1,
                "MaxChoices": 1,
                "Options": [
                    "10",
                    "100",
                    "1,000",
                    "10,000",
                    "100,000",
                    "250,000",
                    "500,000",
                    "750,000",
                    "1,000,000",
                    "5,000,000",
                    "10,000,000+"
                ]
            },
            "DisplayHints": {
                "Title": "Monthly SMS volume",
                "ShortDescription": "Estimated number of SMS messages which will be sent from this sender ID each month."
            }
        }

Prerequisites

Before running either script, you must have the following:

Automate sender ID registration for Indonesia

The Indonesia registration process involves several additional requirements that vary based on your company’s location and business type. All companies must submit XL Axiata’s Letter of Authorization (LOA). Indonesian companies need additional LOAs from Telkomsel, IOH, and Smartfren, plus NIB and NPWP documents. Apply IDR9K and the company stamp to each LOA. For sample documents, refer to Indonesia sender ID registration in AWS End User Messaging SMS. Businesses operating in the money lending sector are required to provide an operating license issued by the Financial Services Authority (Otoritas Jasa Keuangan—OJK).

In this section, we break down the Python script for Indonesia registration.

Before running the registration script, you must first set the necessary variables, with your company actual data. The following is a sample file with the variables required for Indonesia local sender ID registration. Provide the correct path for LOA files (telkomsel_loa.png,ioh_loa.png, xl_axiata_loa.png, smartfren_loa.png,regulatory_licence.png, proof_of_sender_id.png, nomor_pokokWajib_pajak_document.png, and nomor_induk_berusaha_document.png).

Save the following file as indonesia_config.py:

# =============================================================================
# INDONESIA SENDER ID REGISTRATION CONFIGURATION
# =============================================================================
#AWS Region Details
# ---------------------
REGION_NAME='us-west-2'
# Registration Settings
# ---------------------
REGISTRATION_TYPE = 'ID_SENDER_ID_REGISTRATION'  # Registration type for Indonesia
REGISTRATION_NAME = 'INDONESIA_TEST_SENDER_ID'    # Name tag for this registration
# Sender ID Information
# --------------------
# The sender ID to register. Must be between 3 and 11 alphanumeric characters.
# Must contain at least one letter. Example: 'EXAMPLE'
SENDER_ID = 'DEMO'
# Company Information
# ------------------
# Legal name of your company
COMPANY_NAME = 'Example Corp'
# Legal identification number of your company (such as EIN or VAT)
# Must be alphanumeric, 1-30 characters
COMPANY_ID = '123456789'
# Full URL of your company's website
COMPANY_WEBSITE = 'https://www.example.com'
# Select the vertical which most closely aligns with your company's area of business
# Options: Agriculture, Communication, Construction, Education, Energy, Entertainment,
# Financial, Government, Healthcare, Hospitality, Insurance, Manufacturing,
# Real estate, Retail, Technology, Other
AREA_OF_BUSINESS = ['Other']
# Company Address
# ---------------
# Physical street address associated with your company
COMPANY_ADDRESS = '123 Main Street'
# City where the physical address is located
COMPANY_CITY = 'Jakarta'
# Two-digit ISO country code where the physical address is located
COUNTRY_CODE = 'ID'
# Contact Information
# ------------------
# Email address of your company's point of contact
CONTACT_EMAIL = '[email protected]'
# Phone number of your company's point of contact
CONTACT_PHONE = '+6281234567890'
# Messaging Use Case
# -----------------
# Description of your use case for sending SMS messages with this sender ID
USE_CASE_DESCRIPTION = 'For One Time Messages'
# Select the category which most closely aligns with your use case
# Options: One-time passcodes, Account or security alerts, Purchase or delivery notifications,
# Public service announcements, Polling and surveys, Info on demand, Promotions and marketing, Other
USE_CASE_CATEGORY = ['One-time passcodes']
# Estimated number of SMS messages which will be sent from this sender ID each month
# Options: 10, 100, 1,000, 10,000, 100,000, 1,000,000, 10,000,000+
MONTHLY_MESSAGE_VOLUME = ['10,000']
# At least one sample is required of an SMS message which will be sent from this sender ID
# Maximum 306 characters
MESSAGE_SAMPLE = 'Your OTP is XXX'
# Document Paths
# --------------
# Update these paths to point to your actual document files
DOCUMENT_PATHS = {
    # Letter of authorization for Telkomsel (CONDITIONAL - required if company is local to Indonesia)
    # Download, complete, and attach the LOA from AWS documentation
    'TELKOMSEL_LOA': 'telkomsel_loa.png',
    
    # Letter of authorization for IOH (CONDITIONAL - required if company is local to Indonesia)
    # Download, complete, and attach the LOA from AWS documentation
    'IOH_LOA': 'ioh_loa.png',
    
    # Letter of authorization for XL Axiata (REQUIRED for all companies)
    # Download, complete, and attach the LOA from AWS documentation
    'XL_AXIATA_LOA': 'xl_axiata.png',
    
    # Letter of authorization for Smartfren (CONDITIONAL - required if company is local to Indonesia)
    # Download, complete, and attach the LOA from AWS documentation
    'SMARTFREN_LOA': 'smartfren_loa.png',
    
    # Regulatory agency license (OPTIONAL - required only if company's area of business is money lending)
    # Provide operating license from OJK (Otoritas Jasa Keuangan)
    'REGULATORY_LICENSE': 'regulatory_license.png',

Save the following file as indonesia_senderid_registration.py:

import boto3
from typing import Dict, Union, List
import logging
import importlib.util
import argparse
import time
# Set up logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
def load_config(config_file):
    """Load configuration from a Python file"""
    spec = importlib.util.spec_from_file_location("config", config_file)
    config = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(config)
    return config
class EndUserMessagingRegistrationIndonesia:
    def __init__(self, config):
        self.client = boto3.client('pinpoint-sms-voice-v2',region_name=config.REGION_NAME)
        self.registration_id = None
        self.config = config
        self.max_retries = 5
        self.retry_delay = 2
    def create_registration(self) -> str:
        """Create a new Sender ID registration"""
        try:
            response = self.client.create_registration(RegistrationType=self.config.REGISTRATION_TYPE,
                                                       Tags=[{'Key': 'Name', 'Value': self.config.REGISTRATION_NAME}])
            self.registration_id = response['RegistrationId']
            logger.info(f"Registration created with ID: {self.registration_id}")
            return self.registration_id
        except Exception as e:
            logger.error(f"Failed to create registration: {str(e)}")
            raise


    def create_attachment(self, file_path: str) -> str:
        """Create and upload attachments"""
        try:
            with open(file_path, 'rb') as file:
                response = self.client.create_registration_attachment(
                    AttachmentBody=file.read()
                )
            attachment_id = response['RegistrationAttachmentId']
            logger.info(f"Created attachment with ID: {attachment_id}")
            return attachment_id
        except Exception as e:
            logger.error(f"Failed to create attachment: {str(e)}")
            raise
    def wait_for_attachment(self, attachment_id: str) -> bool:
        """Wait for attachment upload to complete"""
        for attempt in range(self.max_retries):
            try:
                response = self.client.describe_registration_attachments(
                    RegistrationAttachmentIds=[attachment_id]
                )
                status = response['RegistrationAttachments'][0]['AttachmentStatus']
                if status == 'UPLOAD_COMPLETE':
                    return True
                logger.info(f"Attachment status: {status}, waiting...")
                time.sleep(self.retry_delay)
            except Exception as e:
                logger.error(f"Error checking attachment status: {str(e)}")
                time.sleep(self.retry_delay)
        return False
def update_registration_fields(self, fields: Dict[str, Union[str, List[str], Dict[str, str]]]):
        """Update registration fields with provided values"""
        if not self.registration_id:
            raise ValueError("Registration ID not set. Create registration first.")
        for field_path, value in fields.items():
            try:
                if isinstance(value, dict) and 'attachmentId' in value:
                    self.client.put_registration_field_value(
                        RegistrationId=self.registration_id,
                        FieldPath=field_path,
                        RegistrationAttachmentId=value['attachmentId']
                    )
                elif isinstance(value, list):
                    self.client.put_registration_field_value(
                        RegistrationId=self.registration_id,
                        FieldPath=field_path,
                        SelectChoices=value
                    )
                else:
                    self.client.put_registration_field_value(
                        RegistrationId=self.registration_id,
                        FieldPath=field_path,
                        TextValue=value
                    )
                logger.info(f"Updated field: {field_path}")
            except Exception as e:
                logger.error(f"Failed to update field {field_path}: {str(e)}")
                raise
    def submit_registration(self):
        """Submit the registration for review"""
        if not self.registration_id:
            raise ValueError("Registration ID not set. Create registration first.")
        try:
            self.client.submit_registration_version(RegistrationId=self.registration_id)
            logger.info("Registration submitted successfully")
        except Exception as e:
            logger.error(f"Failed to submit registration: {str(e)}")
            raise
def main():
    parser = argparse.ArgumentParser(description='Indonesia SMS Registration Tool')
    parser.add_argument('--config', required=True, help='Path to config file')
    args = parser.parse_args()
  config = load_config(args.config)
    registration = EndUserMessagingRegistrationIndonesia(config)
    registration.create_registration()
    # Document mappings
    document_mapping = {
        'TELKOMSEL_LOA': 'idSidSpecificInfo.letterOfAuthorization1',
        'IOH_LOA': 'idSidSpecificInfo.letterOfAuthorization2',
        'XL_AXIATA_LOA': 'idSidSpecificInfo.letterOfAuthorization3',
        'SMARTFREN_LOA': 'idSidSpecificInfo.letterOfAuthorization4',
        'REGULATORY_LICENSE': 'idSidSpecificInfo.regulatoryAgencyLicense',
        'PROOF_OF_SENDER_ID': 'senderIdInfo.proofOfSenderIdConnection',
        'NOMOR_POKOK_WAJIB_PAJAK_Document': 'idSidSpecificInfo.nomorPokokWajibPajakDocument',
        'NOMOR_INDUK_BERUSAHA_DOCUMENT': 'idSidSpecificInfo.nomorIndukBerusahaDocument',
    }
    # Process documents
    document_fields = {}
    for doc_type, file_path in config.DOCUMENT_PATHS.items():
        try:
            attachment_id = registration.create_attachment(file_path)
            if registration.wait_for_attachment(attachment_id):
                if doc_type in document_mapping:
                    field_path = document_mapping[doc_type]
                    document_fields[field_path] = {'attachmentId': attachment_id}
                    logger.info(f"Successfully processed {doc_type}")
        except Exception as e:
            logger.error(f"Failed to process {doc_type}: {str(e)}")
            raise
    # Text fields
    text_fields = {
        'senderIdInfo.senderId': config.SENDER_ID,
        'companyInfo.companyId': config.COMPANY_ID,
        'companyInfo.companyName': config.COMPANY_NAME,
        'companyInfo.website': config.COMPANY_WEBSITE,
        'companyInfo.areaOfBusiness': config.AREA_OF_BUSINESS,
        'companyAddress.address1': config.COMPANY_ADDRESS,
        'companyAddress.city': config.COMPANY_CITY,
        'companyAddress.isoCountryCode': config.COUNTRY_CODE,
        'contactInfo.emailAddress': config.CONTACT_EMAIL,
        'contactInfo.phoneNumber': config.CONTACT_PHONE,
        'messagingUseCase.useCaseCategory': config.USE_CASE_CATEGORY,
        'messagingUseCase.useCaseDescription': config.USE_CASE_DESCRIPTION,
        'messagingUseCase.monthlyMessageVolume': config.MONTHLY_MESSAGE_VOLUME,
        'messagingUseCase.optInDescription': config.USE_CASE_DESCRIPTION,
        'messageSamples.messageSample1': config.MESSAGE_SAMPLE
    }
    # Combine all fields
    all_fields = {**text_fields, **document_fields}
    registration.update_registration_fields(all_fields)
    registration.submit_registration()
if __name__ == "__main__":
    main()

Run the script: python indonesia_senderid_registration.py --config indonesia_config.py

Automate sender ID registration for India

Starting April 30, 2025, AWS will offer India sender ID registration through two Regions: Asia Pacific (Mumbai) and Asia Pacific (Hyderabad).

The sender ID registration process for India differs slightly; it doesn’t require an LOA attachment and includes additional fields specific to the Indian regulatory environment.

Before running the registration script, you must first set the necessary variables with your company’s actual data. The following is a sample file with the variables required for India sender ID registration. Modify and save the file as india_config.py:

# =============================================================================
# INDIA SENDER ID REGISTRATION CONFIGURATION
# =============================================================================
#AWS Region Details
# ---------------------
REGION_NAME='ap-south-1'
# Registration Settings
# ---------------------
REGISTRATION_TYPE = 'IN_SENDER_ID_REGISTRATION'  # Registration type for India
REGISTRATION_NAME = 'INDIA_TEST_SENDERID'        # Name tag for this registration
# Sender ID Information
# --------------------
# The sender ID to register. India sender IDs must be 3-6 alphabetic characters.
# Must exactly match the sender ID registered with TRAI (Telecom Regulatory Authority of India)
SENDER_ID = 'DEMO'
# Principal Entity ID (PEID) - REQUIRED
# The PEID received after completing registration with TRAI
ENTITY_ID = '123'
# Chain IDs (India Specific) - ALL REQUIRED
# ----------------------------------------
# Provide approved chain IDs from your DLT platform after creating telemarketer chains
# Chain ID for ROUTE LEDGER TECHNOLOGIES PRIVATE LIMITED
CHAIN_ID_1 = '456'
# Chain ID for Karix Mobile Pvt Ltd  
CHAIN_ID_2 = '789'
# Chain ID for Sinch Cloud Communication Services India Private Limited
CHAIN_ID_3 = '910'
# Chain ID for Infobip India Private Limited
CHAIN_ID_5 = '912'
# Company Information
# ------------------
# Legal name of your company
COMPANY_NAME = 'Example Corp'
# Legal identification number of your company (such as EIN or VAT)
# Must be alphanumeric, 1-30 characters
COMPANY_ID = '123456789'
# Full URL of your company's website
COMPANY_WEBSITE = 'https://www.example.com'
# Select the vertical which most closely aligns with your company's area of business
# Options: Agriculture, Communication, Construction, Education, Energy, Entertainment,
# Financial, Government, Healthcare, Hospitality, Insurance, Manufacturing,
# Real estate, Retail, Technology, Other
AREA_OF_BUSINESS = ['Other']
# Company Address
# ---------------
# Physical street address associated with your company
COMPANY_ADDRESS = '123 Main Street'
# City where the physical address is located
COMPANY_CITY = 'Any Town'
# Two-digit ISO country code where the physical address is located
COUNTRY_CODE = 'IN'
# Contact Information
# ------------------
# Email address of your company's point of contact
CONTACT_EMAIL = '[email protected]'
# Phone number of your company's point of contact
CONTACT_PHONE = '+12605550100'
# Messaging Use Case
# -----------------
# Description of your use case for sending SMS messages with this sender ID
USE_CASE_DESCRIPTION = 'For One Time Messages'
# Select the category which most closely aligns with your use case
# Options: One-time passcodes, Account or security alerts, Purchase or delivery notifications,
# Public service announcements, Polling and surveys, Info on demand, Other
# Note: India does not support 'Promotions and marketing' category
USE_CASE_CATEGORY = ['One-time passcodes']
# Estimated number of SMS messages which will be sent from this sender ID each month
# Options: 10, 100, 1,000, 10,000, 100,000, 1,000,000, 10,000,000+
MONTHLY_MESSAGE_VOLUME = ['10,000']
# At least one sample is required of an SMS message which will be sent from this sender ID
# Maximum 306 characters
MESSAGE_SAMPLE = 'Your OTP is XXX'
# India Specific Settings
# ----------------------
# Acknowledgement that you will specify Entity ID and Template ID when sending messages
# This is REQUIRED and must be 'Yes'
SENDING_PARAMETERS_ACKNOWLEDGMENT = ['Yes']

The following script handles the creation of the registration, updating of India-specific fields, and submission of the registration. Save the following file as india_senderid_registration.py:

import boto3
from typing import Dict, Union, List
import logging
import importlib.util
import argparse
# Set up logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
def load_config(config_file):
    """Load configuration from a Python file"""
    spec = importlib.util.spec_from_file_location("config", config_file)
    config = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(config)
    return config
class EndUserMessagingRegistrationIndia:
    def __init__(self, config):
        self.client = boto3.client('pinpoint-sms-voice-v2',region_name=config.REGION_NAME)
        self.registration_id = None
        self.config = config
    def create_registration(self) -> str:
        """Create a new Sender ID registration"""
        try:
            response = self.client.create_registration(RegistrationType=self.config.REGISTRATION_TYPE,
                                                       Tags=[{'Key': 'Name', 'Value': self.config.REGISTRATION_NAME}])
            self.registration_id = response['RegistrationId']
            logger.info(f"Registration created with ID: {self.registration_id}")
            return self.registration_id
        except Exception as e:
            logger.error(f"Failed to create registration: {str(e)}")
            raise
    def update_registration_fields(self, fields: Dict[str, Union[str, List[str]]]):
        """Update registration fields with provided values"""
        if not self.registration_id:
            raise ValueError("Registration ID not set. Create registration first.")
for field_path, value in fields.items():
            try:
                if isinstance(value, list):
                    self.client.put_registration_field_value(
                        RegistrationId=self.registration_id,
                        FieldPath=field_path,
                        SelectChoices=value
                    )
                else:
                    self.client.put_registration_field_value(
                        RegistrationId=self.registration_id,
                        FieldPath=field_path,
                        TextValue=value
                    )
                logger.info(f"Updated field: {field_path}")
            except Exception as e:
                logger.error(f"Failed to update field {field_path}: {str(e)}")
                raise
    def submit_registration(self):
        """Submit the registration for review"""
        if not self.registration_id:
            raise ValueError("Registration ID not set. Create registration first.")
        try:
            self.client.submit_registration_version(RegistrationId=self.registration_id)
            logger.info("Registration submitted successfully")
        except Exception as e:
            logger.error(f"Failed to submit registration: {str(e)}")
            raise
def main():
    parser = argparse.ArgumentParser(description='SMS Registration Tool')
    parser.add_argument('--config', required=True, help='Path to config file')
    args = parser.parse_args()
    
    config = load_config(args.config)
    registration = EndUserMessagingRegistrationIndia(config)
    registration.create_registration()
    fields = {
        'senderIdInfo.senderId': config.SENDER_ID,
        'inSidSpecificInfo.principalEntityId': config.ENTITY_ID,
        'inSidSpecificInfo.chainId1': config.CHAIN_ID_1,
        'inSidSpecificInfo.chainId2': config.CHAIN_ID_2,
        'inSidSpecificInfo.chainId3': config.CHAIN_ID_3,
        'inSidSpecificInfo.chainId5': config.CHAIN_ID_5,
        'inSidSpecificInfo.sendingParametersAcknowledgement': config.SENDING_PARAMETERS_ACKNOWLEDGMENT,
        'companyInfo.companyId': config.COMPANY_ID,
        'companyInfo.companyName': config.COMPANY_NAME,
        'companyInfo.website': config.COMPANY_WEBSITE,
        'companyInfo.areaOfBusiness': config.AREA_OF_BUSINESS,
        'companyAddress.address1': config.COMPANY_ADDRESS,
        'companyAddress.city': config.COMPANY_CITY,
        'companyAddress.isoCountryCode': config.COUNTRY_CODE,
        'contactInfo.emailAddress': config.CONTACT_EMAIL,
        'contactInfo.phoneNumber': config.CONTACT_PHONE,
        'messagingUseCase.useCaseCategory': config.USE_CASE_CATEGORY,
        'messagingUseCase.useCaseDescription': config.USE_CASE_DESCRIPTION,
        'messagingUseCase.monthlyMessageVolume': config.MONTHLY_MESSAGE_VOLUME,
        'messagingUseCase.optInDescription': config.USE_CASE_DESCRIPTION,
        'messageSamples.messageSample1': config.MESSAGE_SAMPLE
    }
    registration.update_registration_fields(fields)
    registration.submit_registration()
if __name__ == "__main__":
    main()

Run the script: python india_senderid_registration.py --config india_config.py

After submission, you can monitor the registration status. Upon approval, the status will show as Complete, as shown in the following screenshot.

The following screenshot shows that the registration requires updates before it can be approved.

The error occurred because the registration code was run in an Region other than AP-SOUTH-1 or AP-SOUTH-2. To resolve this issue, delete the current registration and rerun the process in one of the supported Regions.

As mentioned earlier, DescribeRegistrationFieldDefinitions varies by country, because each has unique registration requirements and field specifications. You must modify this script according to your target country’s specific requirements. Refer to the API documentation for country-specific registration types and field definitions.

Check registration status

Check the status of your registration using either the AWS End User Messaging console or the DescribeRegistrations API. To use the console, choose Registrations under Configurations in the navigation pane.

The registration status for each request will initially display CREATED and will change to REVIEWING within 24 hours after submission. For more information about registration statuses, refer to Check a registration’s status in AWS End User Messaging SMS. If your registration status shows REQUIRES_UPDATES, the registration needs more information. You can edit and resubmit the request with the required information.

As noted earlier, third-party reviewers evaluate registrations. Expect 2-4 weeks for approval, and longer for sender IDs in some countries.

SMS program registrations for ISVs

Independent software vendors (ISVs) are positioned between AWS End User Messaging and the ISV’s end business customers. Though they might operate differently or offer different services, their requirements for SMS program registrations are largely the same. End business refers to your ISV customers. This is generally the entity that creates the messaging content, distributes it through your platform, and interacts with their end-users (message recipients).

SMS program registrations require end-user business information, not ISV information. This means the ISV must provide a mechanism for their end businesses to provide their information to be submitted for registration. ISVs and aggregators must provide information representing the customer entity sending messages to opted-in recipients. Amazon uses this information in accordance with all applicable obligations, and to verify the end-user is a legitimate business. Amazon will not contact the end-business user with the information provided.

Conclusion

In this post, we showed how to automate the sender ID registration process using Python scripts and AWS End User Messaging APIs. Using these APIs can significantly improve efficiency and reduce manual errors. For additional guidance, refer to automating AWS End User Messaging US Toll-Free Number registrations, Automate AWS End User Messaging US toll-free Number Registrations, How to Register a Sender ID Using APIs with AWS End User Messaging, and the AWS End User Messaging V2 API Reference.


About the authors

Enhance email security using VPC endpoints with Amazon SES Manager

Post Syndicated from Gabrielle Zhou original https://aws.amazon.com/blogs/messaging-and-targeting/enhance-email-security-using-vpc-endpoints-with-amazon-ses-manager/

Organizations managing on-premises email infrastructure face a critical challenge: how to modernize email systems while maintaining strict security and compliance standards. For healthcare providers, financial institutions, and government agencies, email messages often contain sensitive data that must remain on private networks throughout processing.

The virtual private cloud (VPC) endpoint feature of Amazon Simple Email Service (Amazon SES) Mail Manager addresses this challenge by enabling SMTP messages to remain on your private network throughout processing, routing, and compliance logging before final delivery. This post walks you through implementing this solution to securely modernize your email infrastructure.

Consider this scenario: You’re responsible for a healthcare organization’s email infrastructure that processes thousands of patient communications daily. Your on-premises Exchange servers are aging, maintenance costs are climbing, and your organization is moving workloads to AWS. Your security team requires that email processing for sensitive patient communications—including workflow processing, temporary storage, rule-based routing, and compliance logging—remain within private, controlled networks until ready for final delivery. The Amazon SES Mail Manager VPC endpoint feature addresses this requirement by maintaining network-level isolation for email operations from generation through processing, minimizing data exposure, meeting compliance requirements, and providing defense-in-depth security before final message delivery.

This post demonstrates how to implement VPC endpoints with Amazon SES Mail Manager using exercises from the Amazon SES Mail Manager workshop. We show how to configure VPC endpoints, security groups, and ingress endpoints to maintain private network connectivity for your email processing workflows.

Solution overview

Our approach combines AWS services to create a secure, private email infrastructure:

This solution requires your applications to run within a VPC or have established connectivity between your on-premises network and Amazon VPC through AWS Direct Connect or VPN. For guidance on connecting on-premises networks to AWS, refer to Hybrid network connections.

The following diagram illustrates the solution architecture.

The workflow consists of the following steps:

  1. Amazon Elastic Compute Cloud (Amazon EC2) instances running the sender email application on subnet 10.0.0.0/18 connect to the Amazon SES Mail Manager ingress endpoint through a VPC endpoint.
  2. Sender credentials are retrieved securely from Secrets Manager.
  3. AWS KMS decrypts credentials using your managed encryption keys.
  4. Authenticated email traffic flows securely to SES Amazon SES Mail Manager.

Prerequisites

Before beginning your migration, ensure you have the following:

  • AWS account – Use an AWS account with appropriate permissions for creating and managing a VPC, Secrets Manager, AWS KMS, and Amazon SES. Make sure AWS Identity and Access Management (IAM) policies follow least privilege principles.
  • Existing VPC infrastructure – Use a VPC that hosts your applications in the same AWS account and AWS Region as Amazon SES. For more information, see Plan your VPC.
  • Amazon SES configured – Configure Amazon SES in the same Region and AWS account.
  • Network connectivity – Deploy application servers either on premises with network connectivity to your VPC using Direct Connect or VPN, or already running within the VPC.

For this example, we use Linux SMTP commands from an EC2 instance in the VPC to connect to the Amazon SES Mail Manager ingress endpoint through a VPC endpoint on port 587.

Create traffic policy

Create an Amazon SES Mail Manager traffic policy to filter incoming messages by a combination of recipient address, sender IP address range, and TLS protocol version (1.2 or 1.3). For more details about Amazon SES Mail Manager traffic policies, refer to Traffic policies and policy statements. In this example, we use a traffic policy with minimum TLS version of 1.2.

Complete the following steps:

  1. Open the Amazon SES console in the target Region.
  2. In the navigation pane, under SES Mail Manager, choose Traffic policies.
  3. Choose Create traffic policy.
  4. For Policy name, enter a descriptive name, such as first-traffic-policy.
  5. For Default action, choose Deny.
  6. Choose Add new policy statement.
  7. For Allow or deny properties, choose Allow.
  8. For Properties, choose TLS protocol version.
  9. For Operator, choose Minimum version.
  10. For Value, choose TLS 1.2.
  11. Choose Create traffic policy.

Create rule set

Rule sets are containers for an ordered set of rules that determine how the messages are processed. For more information, see Rule sets and rules. In this example, we use the archive rule to archive all emails processed by Amazon SES Mail Manager.

Complete the following steps:

  1. On the Amazon SES console, under Amazon SES Mail Manager in the navigation pane, choose Rule sets.
  2. Choose Create rule set.
  3. Name the rule (for example, first-rule-set) and choose Create rule set.
  4. Choose Create new rule, then choose Create new rule again.
  5. Under Rule settings, name the rule (for example, archive_all).
  6. Under Actions, choose Add new action.
  7. Chose Archive.
  8. Choose Create archive.
  9. Give the archive a name, such as archive_all.
  10. Set a retention period (3 months for testing).
  11. Choose Create archive.

  1. For Archive resource name, choose archive_all.
  2. Choose Save rule set.

Create security group

Complete the following steps to create a security group:

  1. On the Amazon VPC console, under Security in the navigation pane, choose Security groups.
  2. Choose Create security group.
  3. For Security group name, provide a name that uniquely identifies the security group. For this example, we name the security group my-sg-mail-manager.
  4. For Description, describe the purpose of this security group.
  5. For VPC, choose the VPC that hosts your applications.
  6. For Inbound rules, choose Add rule.
  7. For Type, choose SMTP.
  8. For Source, enter the IP range of your private subnet.
  9. Choose Add rule again.
  10. For Port range, enter 587 and the IP range of your private subnet.
  11. Choose Create security group.

Create VPC endpoint

VPC endpoints make it possible to keep your email traffic within your private AWS network. Complete the following steps to create a VPC endpoint:

  1. Open the Amazon VPC console in the target Region.
  2. Under PrivateLink and Lattice in the navigation pane, choose Endpoints.
  3. Choose Create endpoint.
  4. For Name tag, enter an optional tag, such as mm-vpce-auth-ingress-endpoint.
  5. Select AWS services.
  6. For Services, enter mail-manager to search for Amazon SES Mail Manager VPC endpoints.
  7. Select com.amazonaws.us-east-1.mail-manager-smtp.auth.fips.

  1. For VPC, choose the VPC that hosts your applications.
  2. For DNS name, select Enable DNS name
  3. For DNS record IP type, select IPv4.
  4. Under Subnets, select all Availability Zones and choose the subnet ID for each subnet.
  5. For IP type, select IPv4.

  1. For Security groups, select the group my-sg-mail-manager.
  2. Choose Create endpoint.

Create ingress endpoint

Complete the following steps to create an authenticated ingress endpoint using Secrets Manager and AWS KMS:

  1. On the Amazon SES console, under Amazon SES Mail Manager in the navigation pane, choose Ingress endpoints.
  2. Choose Create ingress endpoint.
  3. For Ingress endpoint name, enter a unique name for the ingress endpoint. For this example, we use my-authenticated-ingress-endpoint.
  4. For Type, choose Authenticated.
  5. For Authentication type, choose Secret.
  6. Choose Create new, which will open a new tab.
  7. For Secret type, choose Other type of secret.
  8. Under Key/value pairs, enter password as the key (anything else will cause authentication to fail), then enter a password as the value.
  9. For Encryption Key, choose Add new key, which will open a new tab.
  10. Choose Create key.
  11. Keep the default values for Key type and Key usage and choose Next.

  1. For Alias, enter a unique name for your custom managed key. For this example, we use my-mail-manager-key.
  2. For Description, describe the purpose of the key.
  3. Choose Next.

  1. For Key administrators, choose any users (other than yourself) or roles you want to permit to administer the key, then choose Next.
  2. For Key users, choose any users (other than yourself) or roles you want to permit to use the key, then choose Next.
  3. For Key policy, choose Edit, then enter the following KMS key policy into the key policy JSON text editor at the "statement" level by adding it as an additional statement separated by a comma. Replace the Region and account number with your own.
{
    "Effect": "Allow",
    "Principal": {
        "Service": "ses.amazonaws.com"
    },
    "Action": "kms:Decrypt",
    "Resource": "*",
    "Condition": {
        "StringEquals": {
           "kms:ViaService": "secretsmanager.us-east-1.amazonaws.com",
            "aws:SourceAccount": "000000000000"
        },
        "ArnLike": {
            "aws:SourceArn": "arn:aws:ses:us-east-1:000000000000:mailmanager-ingress-point/*"
        }
    }
}
  1. Choose Next.
  2. Review and choose Finish.
  3. Switch to the Secrets Manager tab and choose the refresh icon.
  4. Choose the KMS key you just created, then choose Next.

  1. For Secret name, provide a unique name for the secret. For this example, we use my-mail-manager-secret.
  2. For Description, describe the purpose for the secret.
  3. For Resource permissions, replace the example JSON code in the editor with the following policy. Replace the Region and the account number with your own.
{
    "Version": "2012-10-17",
    "Id": "Id",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "ses.amazonaws.com"
            },
            "Action": "secretsmanager:GetSecretValue",
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": "000000000000"
                },
                "ArnLike": {
                    "aws:SourceArn": "arn:aws:ses:us-east-1:000000000000:mailmanager-ingress-point/*"
                }
            }
        }
    ]
}
  1. Choose Save, then choose Next.
  2. Configuring automatic rotation is optional. We skip this step and choose Next.
  3. Review and choose Store.
  4. Switch back to the Amazon SES console tab to finish creating the ingress endpoint.
  5. For Secret ARN, choose Refresh list, then choose the secret you just created.
  6. For Rule set, choose first-rule-set.
  7. For Traffic policy, choose first-traffic-policy.
  8. For Network type, select Private.
  9. For VPC endpoint ID, choose mm-vpce-auth-ingress-endpoint.
  10. Choose Create ingress endpoint.

Test configuration

Complete the following steps to test your configuration:

  1. Open the Amazon VPC console in the target Region.
  2. Under PrivateLink and Lattice in the navigation pane, choose Endpoints.
  3. Choose the VPC endpoint ID of mm-vpce-auth-ingress-endpoint to open the details page.
  4. Find the DNS names for the VPC endpoint. The first DNS name on this list is the Regional DNS name of the VPC endpoint; copy this DNS name and save it on a notepad for later use.

  1. Open the Amazon SES console in the target Region.
  2. Under Amazon SES Mail Manager in the navigation pane, choose Ingress endpoints.
  3. Choose my-authenticated-ingress-endpoint.
  4. In the Authentication section, locate the SMTP user name (typically starts with inp-).

  1. Connect to your EC2 instance using SSH.
  2. Use the command line to send an email using the Amazon SES SMTP interface to test the connectivity. Replace the endpoint with the DNS name of the VPC endpoint you copied earlier.

The message 250 OK esllb73q6bd94cnq004ujd544f169sog39bc9ug1 indicates the message was successfully accepted by Amazon SES Mail Manager.

Clean up

When you’re done with this solution, clean up the resources you created including Mail Manager configurations, security groups, VPC endpoints, KMS keys, and Secrets Manager secrets to avoid additional charges.

Conclusion

In this post, we showed how to enhance email security by implementing Amazon SES Mail Manager with VPC endpoints. This solution can help you modernize your email infrastructure while maintaining network-level isolation and meeting enterprise compliance requirements.

To learn more about Amazon SES, see the Amazon SES Developer Guide. For additional security best practices, refer to AWS Best Practices for Security, Identity, & Compliance. To get started using Amazon SES Mail Manager, participate in an Amazon SES Mail Manager workshop event, explore the advanced workflow features of Amazon SES Mail Manager, and consider integrating with your existing monitoring and alerting systems.


About the authors

Implement Tenants in your Amazon SES environment, Part 3: Implementation guide

Post Syndicated from Rommel Sunga original https://aws.amazon.com/blogs/messaging-and-targeting/implement-tenants-in-your-amazon-ses-environment-part-3-implementation-guide/

This is part 3 in a series covering the new tenants feature in Amazon Simple Email Service (SES). The first post in this series discussed how users can improve email deliverability with tenant management in Amazon SES. Part 2 covered key aspects involved in planning the migration of existing Amazon SES infrastructure to use tenant-based reputation isolation.

With Amazon SES tenants, users can:

  • Manage individual tenant onboardings and their reputations in isolation
  • Provision isolated tenants within a single SES account
  • Apply automated reputation policies to manage email sending
  • Detect and isolate deliverability issues within isolated email streams
  • Preserve sender reputation and improve inbox placement with mailbox providers

This post provides a step-by-step migration guide to Tenants for key AWS components like AWS Identity and Access Management (IAM) permissions, Amazon CloudWatch logging and Amazon EventBridge monitoring. Additionally, we provide several code examples that show how to programmatically provision tenants in real time as customers are onboarded. The goal is to demonstrate how to use the Tenants feature to achieve reputation isolation between customers or business units (BUs), get more control over sending policies, and enable the automatic pause mechanism to limit the damage from problematic senders.

Step-by-step migration guide

Having completed the inventory and configuration planning prescribed in part 2 of this series, we are ready to start the 4-step migration (or implementation) of the tenants feature in the AWS SES account.

The Amazon SES Tenants Migration process is as follows:

  1. Preparation
    1. Verify SES V2 API usage
    2. Update SDK versions
  2. Create Your First Tenant
    1. Create Tenant API calls
    2. Implement in onboarding workflow
    3. Verify tenant creation
  3. Associating Resources
    1. Link verified domains to tenants
    2. Associate configuration sets
    3. Connect IP pools
  4. Updating Your Sending Code
    1. Add Tenant/Name parameter to API calls
    2. Add X-SES-TENANT header for SMTP
    3. Update application sending code
    4. Test email sending functionality

Preparation

When sending email through a tenant, be sure to specify the tenant in the API calls or SMTP headers and ensure that all resources used are associated with that tenant. Before getting started, keep in mind that there’s an additional charge per tenant per month based on the number of emails. For more detailed information, see the SES Pricing page.

For applications that use SMTP, see the SMTP Implementation section later in this document.

For applications that use the SES API, confirm that the latest Amazon SES V2 API is being used, as tenant management capabilities are only available in this version (see SES V2 API migration guide). We also recommend verifying that the AWS SDK version being used supports these operations, otherwise you may need to update to a version that supports them.

The SES V2 API includes the seven essential operations for managing the tenant architecture that the application will need to leverage throughout the migration (or implementation) and tenant lifecycle:

  1. CreateTenant for establishing new tenant containers
  2. CreateTenantResourceAssociation for linking resources like domains and configuration sets to tenants
  3. DeleteTenant for removing tenants when workloads offboard
  4. DeleteTenantResourceAssociation to unlink resources from tenants
  5. GetTenant retrieves specific tenant details
  6. ListTenants provides an overview of all tenants in an account
  7. ListTenantResources shows which resources are associated with each tenant

Implementation Steps

Creating the tenant(s)

The following Python code example uses the AWS SDK for Python (Boto3) and CreateTenant to demonstrate tenant creation. We’ve added optional tags to better organize the tenant resource for billing or logging purposes.

import boto3
from botocore.exceptions import ClientError

def setup_ses_tenant():
    ses_client = boto3.client('sesv2')
    
    # Create a new tenant with descriptive tags
    tenant_response = ses_client.create_tenant(
        TenantName='MyTenant',
        Tags=[
            {
                'Key': 'Environment',
                'Value': 'Production'
            },
            {
                'Key': 'CustomerID',
                'Value': '[customer_id]'
            }
        ]
    )
    
    # Verify tenant creation
    tenants = ses_client.list_tenants()
    print(f"Total tenants in account: {len(tenants['Tenants'])}")
    
    return tenant_response['TenantName']


if __name__ == "__main__":
    try:
        tenant_name = setup_ses_tenant()
        print(f"Successfully created tenant: {tenant_name}")
    except ClientError as e:
        print(f"AWS error: {e}")
    except Exception as e:
        print(f"Unexpected error: {e}")

This example code can be used when a customer first onboards onto the platform to send email. By creating a tenant for this customer, resources under that tenant will be associated together whenever the tenant is used as explained in the next steps.

Associating resources with the tenant

Each tenant needs appropriate resources—configuration sets, sending identities, and potentially dedicated IP pools—to begin sending email. The association process should align with the resource sharing strategy as established during the migration planning phase.

The following Python code example uses the AWS SDK for Python (Boto3) and CreateTenantResourceAssociation to associate resources to the tenant that was created in the previous step.

import boto3
from botocore.exceptions import ClientError

def associate_resources_with_tenant():
    ses_client = boto3.client('sesv2')
    
    try:
        # Associate verified domain identity
        ses_client.create_tenant_resource_association(
            TenantName='MyTenant',
            ResourceArn='arn:aws:ses:[aws_region]:[account_id]:identity/[domain_name]'
        )
        print("Successfully associated email identity with tenant")

        # Associate configuration set for tracking
        ses_client.create_tenant_resource_association(
            TenantName='MyTenant',
            ResourceArn='arn:aws:ses:[aws_region]:[account_id]:configuration-set/MyTenantConfigurationSet'
        )
        print("Successfully associated configuration set with tenant")

    except ClientError as e:
        print(f"Error: {e.response['Error']['Message']}")
        raise

if __name__ == "__main__":
    associate_resources_with_tenant()

Consider implementing batch association for email streams with multiple domains or configuration sets. This approach reduces API calls and improves provisioning efficiency. Remember that resources can be shared across multiple tenants if the architecture requires it, allowing flexible resource allocation strategies.

Update the applications

The transition to tenant-based sending requires minimal code changes in the apps, namely adding the TenantName and ConfigurationSetName to the sending process.

API Implementations

For applications that use the SES V2 API, add the tenant and configuration set parameters to the send calls as demonstrated below using the AWS SDK for Python (Boto3) :

import boto3

def send_email_from_tenant():
    ses_client = boto3.client('sesv2')
    
    response = ses_client.send_email(
        FromEmailAddress='sender@[domain_name]',
        Destination={
            'ToAddresses': ['[recipient_email]']
        },
        Content={
            'Simple': {
                'Subject': {
                    'Data': 'Test email from SES tenant'
                },
                'Body': {
                    'Text': {
                        'Data': 'This is a test email sent from an SES tenant'
                    }
                }
            }
        },
        ConfigurationSetName='MyTenantConfigurationSet',
        TenantName='MyTenant'  # Critical addition for tenant routing
    )
    
    print(f"Message sent! Message ID: {response['MessageId']}")
    return response

if __name__ == "__main__":
    send_email_from_tenant()

SMTP Implementations

For sending applications that use SMTP, add the X-SES-TENANT and the ConfigurationSetName header parameters to every message. In the code block that follows, we demonstrate the proper header configuration for SMTP sending using Python:

from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import smtplib

def send_smtp_email_with_tenant():
    sender = 'smtp-sender@[domain_name]'
    sender_name = 'Sender Name'
    recipient = '[recipient_email]'
    username_smtp = '[smtp_username]'
    configuration_set = 'MyTenantConfigurationSet'
    host = 'email-smtp.[aws_region].amazonaws.com'
    port = 587
    
    subject = 'Amazon SES test (SMTP interface accessed using Python)'
    body_text = """Email Test
    This email was sent through the Amazon SES SMTP interface using Python."""
    body_html = """<h1>Email Test</h1>
        <p>This email was sent through the 
        <a href="https://aws.amazon.com/ses">Amazon SES</a> SMTP
        interface using Python."</p>"""
    
    msg = MIMEMultipart('alternative')
    msg['Subject'] = subject
    msg['From'] = f"{sender_name} <{sender}>"
    msg['To'] = recipient
    msg['X-SES-CONFIGURATION-SET'] = configuration_set
    
    # Critical: Specify tenant for SMTP sending
    msg['X-SES-TENANT'] = 'MyTenant'
    
    part1 = MIMEText(body_text, 'plain')
    part2 = MIMEText(body_html, 'html')
    msg.attach(part1)
    msg.attach(part2)
    
    try:
        server = smtplib.SMTP(host, port)
        server.ehlo()
        server.starttls()
        server.ehlo()
        server.login(username_smtp, fetch_smtp_password_from_secure_storage())
        server.sendmail(sender, recipient, msg.as_string())
        print("Email sent successfully!")
        
    except Exception as e:
        print(f"Error: {str(e)}")
    
    finally:
        server.quit()

def fetch_smtp_password_from_secure_storage():
    # Implement secure password retrieval from AWS Secrets Manager
    # or your preferred secret storage solution
    return '[smtp_password]'


if __name__ == "__main__":
    send_smtp_email_with_tenant()

Configuring IAM policies and permissions for tenants

This section explains how to configure IAM permissions for SES tenants, including how to set up different permission levels for tenant management, email sending, and monitoring while following security best practices to control access based on organizational roles. Remember that IAM policies for tenants follow the principle of least privilege. Start with minimal permissions and expand as needed, regularly reviewing and removing unused permissions to maintain security.

Tenant management permissions

SES Tenants can be controlled through specific IAM permissions that determine who can create, modify, and use specific tenant(s) in the organization. The tenant management system’s core API actions discussed previously can be granted with granular access through IAM policies for administrative operations. The Service Authorization Reference for Amazon Simple Email Service v2 page contains the latest documentation for the service-specific actions used below for Amazon Simple Email Service.

We recommend limiting each IAM role’s permission based on the minimum capabilities required by that role. This helps mitigate the potential for negative effects if an SMTP credential is misused. What follows is a basic IAM policy that demonstrates full tenant management capabilities; this is NOT demonstrating the principle of least privilege (yet):

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ses:CreateTenant",
                "ses:DeleteTenant",
                "ses:GetTenant",
                "ses:ListTenants",
                "ses:CreateTenantResourceAssociation",
                "ses:DeleteTenantResourceAssociation",
                "ses:ListTenantResources",
                "ses:ListResourceTenants"
            ],
            "Resource": "*"
        }
    ]
}

Configuring sending permissions with tenants

Applications that send emails through tenants need different permissions than those managing tenants. The key distinction is using the ses:TenantName condition to restrict which tenants an application can use for sending.

The IAM policy below allows sending emails only through the specified CustomerA-Tenant tenant, ensuring applications can’t accidentally or maliciously send through other customers’ tenants.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ses:SendEmail",
                "ses:SendBulkEmail"
            ],
            "Resource": [
                "arn:aws:ses:[aws_region]:[account_id]:identity/*",
                "arn:aws:ses:[aws_region]:[account_id]:configuration-set/*"
            ],
            "Condition": {
                "StringEquals": {
                    "ses:TenantName": "CustomerA-Tenant"
                }
            }
        }
    ]
}

Separating administrative and operational access

Production environments implement role separation between tenant management and email sending operations. Administrative roles handle tenant creation and resource association during customer onboarding, while application roles can only send emails through assigned tenants. The IAM policy below is an example of an administrative role; it allows creating and configuring tenants for use during customer onboarding but does not allow the tenant deletion action to prevent accidental deletions.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ses:CreateTenant",
                "ses:ListTenants"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ses:CreateTenantResourceAssociation",
                "ses:GetTenant"
            ],
            "Resource": "arn:aws:ses:[aws_region]:[account_id]:tenant/*/tn-*"
        },
        {
            "Effect": "Deny",
            "Action": "ses:DeleteTenant",
            "Resource": "*"
        }
    ]
}

Resource-level permissions

Tenants support resource-level permissions using Amazon Resource Names (ARNs), enabling fine-grained access control. Grant access to specific tenants by specifying the tenant name and tenant id (ex. CustomerA-Tenant/tn-1a2b3c4d5e6f7890abcdef1234567890) rather than granting blanket permissions using the “*/tn-*” wildcard as above. To obtain the tenant id you can use the list-tenants command of the AWS SESv2 CLI.

The IAM policy below grants access only to tenants CustomerA-Tenant and CustomerB-Tenant where the following tenant id is a placeholder that should be replaced by the correct tenant id that you obtained from list-tenants.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "ses:GetTenant",
            "Resource": [
                "arn:aws:ses:[aws_region]:[account_id]:tenant/CustomerA-Tenant/tn-1a2b3c4d5e6f7890abcdef1234567890",
                "arn:aws:ses:[aws_region]:[account_id]:tenant/CustomerB-Tenant/tn-9876543210fedcba0987654321abcdef"
            ]
        }
    ]
}

Monitoring and compliance access

Security and compliance teams often need read-only access to monitor tenant usage. The following IAM policy grants read-only access to all tenants, but does not permit modifications or deletions of any tenants:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ses:GetTenant",
                "ses:ListTenants",
                "ses:ListTenantResources"
            ],
            "Resource": "*"
        }
    ]
}

Monitoring Tenants with EventBridge and CloudWatch

Amazon SES integrates with Amazon EventBridge to deliver comprehensive monitoring capabilities for tenant management, providing real-time visibility into reputation changes and enabling automated response workflows. EventBridge is a serverless service that uses JSON-formatted events to connect application components, making it straightforward to build scalable event-driven applications. Amazon SES’s tenant feature’s integration with EventBridge enables organizations to track tenant-specific metrics and other metrics, such as reputation findings and reputation status changes, and tenant status changes.

Understanding EventBridge Integration with SES

EventBridge operates as a router that receives events from SES and delivers them to one or many destinations (aka targets). When SES features experience state changes or status updates, they automatically send events to the EventBridge default event bus. Rules associated with the event bus evaluate events as they arrive, checking whether each event matches the rule’s pattern before routing to specified targets. For SES tenant management, organizations can receive real-time alerts through Amazon EventBridge when tenant reputation findings are detected or when tenant status changes occur. These events are delivered on a best-effort basis; they might be delivered out of order, requiring users to deploy processing logic to handle such scenarios gracefully.

The code block that follows can guide users through the basic EventBridge integration with the SES tenant feature. For more extensive documentation on integrating with EventBridge please consult AWS documentation.

Setting up EventBridge integration

Create an EventBridge rule using the AWS SDK for Python (Boto3) to capture tenant status changes and reputation findings:

# Create rule for monitoring tenant status changes
aws events put-rule \
    --name "SESTenantStatusMonitor" \
    --description "Monitor SES tenant status changes" \
    --event-pattern '{
        "source": ["aws.ses"],
        "detail-type": [
            "Sending Status Enabled",
            "Sending Status Disabled",
            "Advisor Recommendation Status Open",
            "Advisor Recommendation Status Closed"
        ]
    }'

Routing events from EventBridge to CloudWatch Logs

CloudWatch provides the capability to collect raw data and process it into readable, near real-time metrics. Follow these steps to set up a CloudWatch log group for SES tenant events and configure the appropriate IAM permissions:

  1. Create a CloudWatch log group for tenant events:

aws logs create-log-group --log-group-name "/aws/ses/tenants"

  1. Add resource-based policy to CloudWatch Logs:
aws logs put-resource-policy \
    --policy-name EventBridgeToCloudWatchLogsPolicy \
    --policy-document '{
        "Version": "2012-10-17",
        "Statement": [{
            "Sid": "TrustEventsToStoreLogEvent",
            "Effect": "Allow",
            "Principal": {
                "Service": ["events.amazonaws.com", "delivery.logs.amazonaws.com"]
            },
            "Action": ["logs:CreateLogStream", "logs:PutLogEvents"],
            "Resource": "arn:aws:logs:[aws_region]: [account_id]:log-group:/aws/ses/tenants:*"
        }]
    }'
  1. Add the EventBridge target:

aws events put-targets \
    --rule "SESTenantStatusMonitor" \
    --targets '[{
        "Id": "SendToCloudWatchLogs",
        "Arn": "arn:aws:logs:[aws_region]:[account_id]:log-group:/aws/ses/tenants"
    }]'

An example event of a paused (“disabled”) tenant is shown below for reference:

{
    "version": "0",
    "id": "3cc76530-9842-03a9-fdef-e4e4f667cf4e",
    "detail-type": "Sending Status Disabled",
    "source": "aws.ses",
    "account": "[account_id]",
    "time": "2025-10-01T03:37:17Z",
    "region": "[aws_region]",
    "resources": [
        "arn:aws:ses:[aws_region]::tenant/CustomerA-Tenant/tn-2a8c678ec0000fdaf76cc1f127b40"
    ],
    "detail": {
        "version": "1.0.0",
        "data": {
            "origin": "CUSTOMER_MANAGED",
            "record": {
                "status": "DISABLED",
                "cause": "Status manually updated.",
                "lastUpdatedTimestamp": [
                    2025,
                    10,
                    1,
                    3,
                    37,
                    17,
                    671000000
                ]
            }
        }
    }
}

Managing tenant reputation with key Tenants features

Reputation management for individual tenants is one of the core benefits of using Amazon SES’s tenant feature, providing automated protection against deliverability issues that could damage overall account reputation. This section demonstrates how to configure reputation policies that can automatically pause tenants when they experience high bounce rates or reputation issues, as well as how to manually control tenant sending status for custom workflows.

Setting reputation policies

Amazon SES provides three reputation policy enforcement levels that determine how the system responds to reputation findings.

  • The Standard policy (recommended) pauses sending only for high-impact findings, providing a balance between protection and operational flexibility.
  • The Strict policy pauses sending for any reputation finding, offering maximum protection for sensitive environments.
  • The None option disables automated pausing while continuing to track findings, useful for manual monitoring scenarios.

Reputation findings are generated in two severity levels—low and high—based on metrics like bounce rates and complaint rates. When these metrics indicate a potential deliverability issue, SES creates findings that can trigger automatic pausing based on a chosen policy.

In the following code block, we demonstrate how to set a reputation policy on the CustomerA-Tenant

import boto3
from botocore.exceptions import ClientError

def update_tenant_reputation_policy(tenant_arn, policy_arn):
    ses_client = boto3.client('sesv2')
    
    ses_client.update_reputation_entity_policy(
        ReputationEntityType='RESOURCE',
        ReputationEntityReference=tenant_arn,
        ReputationEntityPolicy=policy_arn
    )
    print(f"Tenant policy_arn updated to: {policy_arn}")

# Example usage
if __name__ == "__main__":
    tenant_arn = "arn:aws:ses:[aws_region]:[account_id]:tenant/CustomerA-Tenant/tn-2a8c678ec0000fdaf76cc1f127b40"
    
    # Enable normal sending
    update_tenant_reputation_policy(tenant_arn, 'arn:aws:ses:[aws_region]:aws:reputation-policy/standard')
    
    # Disable/pause sending
    update_tenant_reputation_policy(tenant_arn, 'arn:aws:ses:[aws_region]:aws:reputation-policy/strict')
    
    # Reinstate with monitoring
    update_tenant_reputation_policy(tenant_arn, 'arn:aws:ses:[aws_region]:aws:reputation-policy/none')

Our recommendation is to choose the Standard policy for most production tenants, as this policy provides automatic protection against severe reputation issues while avoiding unnecessary disruptions. Reserve the Strict policy for new or untrusted tenants where maximum caution is warranted. Use the None option during initial monitoring periods or when implementing custom reputation management logic.

Handling paused tenants

When a tenant is paused, either automatically through reputation policies or manually, the sending status prevents any emails from being sent through that tenant. The system derives this aggregate status from both customer-managed and AWS-managed statuses; if either is set to DISABLED, the tenant cannot send emails.

Amazon SES publishes notifications to EventBridge when tenant status changes occur or new reputation findings are detected, enabling real-time response to reputation events. After investigating and resolving the underlying issues, the tenant’s sending capabilities can be reinstated. During reinstatement (REINSTATED status), the tenant can continue sending while metrics are monitored to verify improvement.

def update_tenant_status(tenant_arn, status):
    ses_client = boto3.client('sesv2')
    
    ses_client.update_reputation_entity_customer_managed_status(
        ReputationEntityType='RESOURCE',
        ReputationEntityReference=tenant_arn,
        SendingStatus=status
    )
    print(f"Tenant status updated to: {status}")

# Example usage
if __name__ == "__main__":
    tenant_arn = "arn:aws:ses:[aws_region]:[account_id]:tenant/CustomerA-Tenant/tn-2a8c678ec0000fdaf76cc1f127b40"
    
    # Enable normal sending
    update_tenant_status(tenant_arn, 'ENABLED')
    
    # Disable/pause sending
    update_tenant_status(tenant_arn, 'DISABLED')
    
    # Reinstate with monitoring
    update_tenant_status(tenant_arn, 'REINSTATED')

The REINSTATED status allows the tenant to continue sending even with active reputation findings. Once metrics return to healthy levels, the tenant automatically transitions back to ENABLED status. This approach ensures minimal disruption while protecting the overall account reputation from problematic email streams.

Managing tenant lifecycle

When customers modify a service tier or leave the platform, proper cleanup ensures resource efficiency and maintains account organization.

Removing resource associations

Before deleting a tenant, remove all resource associations to prevent orphaned configurations:

import boto3
from botocore.exceptions import ClientError

def remove_resource_from_tenant():
    ses_client = boto3.client('sesv2')
    
    try:
        # Remove identity association
        ses_client.delete_tenant_resource_association(
            TenantName='MyTenant',
            ResourceArn='arn:aws:ses:[aws_region]:[account_id]:identity/[domain_name]'
        )
        print("Successfully removed identity association")
        
        # Remove configuration set association
        ses_client.delete_tenant_resource_association(
            TenantName='MyTenant',
            ResourceArn='arn:aws:ses:[aws_region]:[account_id]:configuration-set/MyTenantConfigurationSet'
        )
        print("Successfully removed configuration set association")
    
    except ClientError as e:
        print(f"Error: {e.response['Error']['Message']}")
        raise


if __name__ == "__main__":
    remove_resource_from_tenant()

Deleting tenants

Once all resources are disassociated, remove the tenant entirely:

import boto3
from botocore.exceptions import ClientError

def delete_tenant(tenant_name):
    ses_client = boto3.client('sesv2')
    
    try:
        # Delete the tenant
        ses_client.delete_tenant(TenantName=tenant_name)
        print(f"Successfully deleted tenant: {tenant_name}")
        return True
        
    except ClientError as e:
        print(f"Error deleting tenant: {e.response['Error']['Message']}")
        raise

# Example usage with error handling
if __name__ == "__main__":
    try:
        delete_tenant("MyTenant")
    except ClientError as e:
        print(f"Cleanup failed: {e}")

Tenant lifecycle management ensures clean transitions when customers change service tiers or leave the platform. Implement these operations in customer offboarding workflows to maintain optimal account organization and resource utilization.

Resource Management

Resource Sharing Capabilities

Resources can be assigned to multiple tenants simultaneously. This enables sharing common resources between tenants while maintaining separate reputation tracking. For example, the reputation for marketing and transactional email could be tracked separately across independent tenants while using the same sending domain. SES validates tenant-resource associations at send time, rejecting requests if the specified tenant lacks access to the requested resources.

Resource Migration Between Tenants

Resource migration involves two API calls. First, remove the association from the current tenant using DeleteTenantResourceAssociation, then create a new association with the target tenant using CreateTenantResourceAssociation. This process can be automated for bulk migrations during reorganizations.

Reputation Management

Tenant Isolation Protection

Each tenant maintains independent reputation metrics and sending status. When one tenant experiences deliverability issues, it can be automatically paused without affecting other tenants’ ability to send, protecting both shared resources and account-level reputation.

Tenant Pausing Triggers

Tenants can either be paused manually using the UpdateReputationEntityCustomerManagedStatus API or paused automatically based on the reputation policy assigned to the tenant. Reputation policies pause tenants based on reputation findings generated from bounce rates, complaint rates, and third-party feedback reports. The Standard policy (recommended) pauses only for high-severity findings (bounce rate > 15%, complaint rate > 1%), while Strict pauses even for low severity findings (bounce rate > 10%, complaint rate > 0.5%).

Tenant Reactivation Process

For tenants paused by automated reputation policies, use the UpdateReputationEntityCustomerManagedStatus API to reinstate sending after addressing root causes. Tenants paused by AWS Trust & Safety require case resolution through AWS Support.

Migrating Existing Customers

Creating tenants for existing email streams can be completed with no disruption to email sending. Start by creating tenants for each customer or business unit, then associate existing resources like email identities, configuration sets, and templates using the tenant association APIs. Once those steps are complete, update the application, or inform the customer or BU they now need to specify the tenant name and configuration set in their SES SendEmail API calls or SMTP headers which enables SES to route emails through the appropriate tenant.

Reputation Metrics Transition

New reputation metrics will be tracked separately for each tenant from the point of creation and use. Historical metrics from before tenant implementation are not available. Tenant metrics contribute to the overall account-level reputation. For example, if tenant-a, tenant-b, and tenant-c each send 1,000 emails and:

  • Tenant-a receives 150 bounce notifications over 1,000 emails (for a bounce rate of 15%), tenant reputation protection will pause this tenant before the issue escalates.
  • Tenant-b receives 0 bounces over 1,000 emails, for a bounce rate of 0%
  • Tenant-c receives 0 bounces over 1,000 emails, for a bounce rate of 0%

The SES Account Level Bounce Rate (all tenants) is 150 out of 3,000, or 5%

Account Activation Requirements

No account-level activation is required to configure and use the Tenant feature, it is immediately available through the SES V2 API or the AWS SES Console. Users can also start using the tenant management APIs (CreateTenant, CreateTenantResourceAssociation, DeleteTenant, etc.) without any account modifications or support requests.

Conclusion

This post covered detailed migration steps, monitoring setup, practical implementation examples and troubleshooting steps. From running a SaaS platform, to managing multiple brands, to operating separate business units, tenant-based reputation isolation ensures Amazon SES email infrastructure scales reliably as an organization grows.

Additional resources


About the authors

Implement Tenants in your Amazon SES environment, Part 2: Assessment and planning

Post Syndicated from Rommel Sunga original https://aws.amazon.com/blogs/messaging-and-targeting/implement-tenants-in-your-amazon-ses-environment-part-2-assessment-and-planning/

Running multiple users or business units (BUs) on a shared email infrastructure often creates deliverability and compliance risks. The tenant-based reputation isolation feature in Amazon Simple Email Service (Amazon SES) solves these challenges by separating email reputation by customer, BU, or workload. This is part 2 in a series covering the new tenants feature in Amazon Simple Email Service (SES). The first post in this series discussed how users can improve email deliverability with tenant management in Amazon SES (for more details, see the blog post).

In this post, we provide an overview of the tenants feature and guidance for planning the implementation to or migration of your existing Amazon SES infrastructure to use tenant-based reputation isolation.

Part 3 covers practical implementation steps for the tenants feature, including configuration steps for key components like Identity and Access Management (IAM) permissions, Amazon CloudWatch logging, and Amazon EventBridge monitoring. We also provide code examples using the Amazon SES v2 APIs that show how to provision tenants in real time as you onboard downstream customers or business divisions onto your Amazon SES account. Key outcomes include complete reputation isolation between customers, control over sending policies, and automated pause mechanisms for problematic senders.

Solution overview

Amazon SES now offers tenant management capabilities that enable isolated email sending environments within a single AWS account. This provides granular reputation management across different email streams. You can assign dedicated configuration sets, sending identities, and templates to each tenant. Each tenant functions as a logical container that maintains its own sending reputation independently. This tenant-based reputation isolation makes sure deliverability issues with one tenant won’t affect your other tenants. You also get real-time visibility into tenant-level metrics, including messages sent, bounce rates, and complaint rates.

The following diagram illustrates the solution architecture.

Tenants are a best practice for those who want visibility, isolation, and control over their email reputation, from large-scale platforms to smaller teams:

  • Independent software vendors (ISVs) with multiple customers, and marketing and software as a service (SaaS) solutions using a central AWS SES account – You can provision a tenant for each customer to segregate email sending and prevent one client’s sending from negatively affecting deliverability for other clients.
  • Enterprises and other large organizations with multiple BUs – You can maintain separate reputation profiles for different BUs, departments, or brands within the same AWS account.
  • Organizations with distinct mail streams – You can separate transactional and marketing mail, or keep product notifications distinct from internal communications, to make sure issues with one tenant’s email stream don’t affect the other tenants.
  • Single-stream senders – Even if you currently only operate one email stream, you can future-proof your SES account by assigning a tenant from day one. This provides visibility into reputation findings and helps you apply proactive policies to stay ahead of deliverability issues before they impact sending.

In the following sections, we outline how to plan and execute your migration to Amazon SES tenants. We show how to assess your current setup, choose the right tenant structure for your organization, and implement a gradual rollout that minimizes disruption to your existing email operations.

Pre-migration assessment

If you are already using Amazon SES, the transition to tenant-based architecture begins with understanding your current email infrastructure. Start by creating an inventory of your existing sending identities (domains) and identify what, if any, Amazon SES configuration set is assigned. This will help you understand how each customer or BU is currently distributed across your Amazon SES infrastructure. Review each configuration set to identify and track the various configuration options, such as the sending IP pool and archive option. The information you uncover in this phase will serve as the blueprint for your tenant associations and help you make sure each tenant has access to the appropriate resources, while maintaining proper isolation boundaries.

Recommended tenant structure: Individual tenants per customer

For most implementations, we recommend creating one or more tenants per customer or BU so you can achieve complete tenant-level isolation of email workloads. With this approach, you can supply dedicated SMTP or API credentials per tenant to provide secure access to allocated resources. You can apply different reputation policies per tenant as needed and automatically pause any tenant that conflicts with the reputation policy. Additionally, the shared tenant monitoring tools in Amazon SES help you automatically monitor and enforce reputation-based policies at the tenant level, so that problematic email sending behavior from one tenant doesn’t impact the deliverability of others.

The following diagram shows an example of an ISV that operates a SaaS platform on AWS that sends emails on behalf of its many customers from a single Amazon SES account. They have followed the recommended approach by implementing a 1:1 mapping between tenants and customers. This strategy provides complete reputation isolation for each customer and makes sure the ISV operating the Amazon SES account can automatically prevent deliverability issues from one customer from impacting others. This architecture minimizes the reputation damage a single tenant can cause to the ISV’s Amazon SES account’s shared resources like IP pools and domains, making it the optimal choice for maintaining high deliverability standards.

Resource sharing strategies

Your resource allocation strategy depends on your business model and customer segmentation. Consider the following recommended approaches:

  • Complete resource isolation – Each customer, brand, BU, or workload is assigned to individual, dedicated resources using the tenant configuration. This approach offers the simplest migration path: associate each customer’s dedicated resources (identities, IP addresses) with their corresponding tenant and continue operations with enhanced isolation. This model works well for enterprise customers and ISVs who deploy dedicated IPs for customers and require complete separation.
  • Tiered resource sharing – Organizations and ISVs with service tiers (such as free, pro, and VIP) can align tenants and resource sharing with these tiers. Free-tier customers might use the free-tier tenant and share basic resources, pro customers access the pro-tier tenant that maps to enhanced shared resources, and VIP customers receive dedicated resources, often using VIP customer-specific tenants. This balances cost-efficiency with appropriate isolation levels for each customer segment.
  • Extensive resource sharing – Even when email streams share most resources in the Amazon SES account, such as sending identities and IP pools, tenant isolation tenant isolation allows you to protect the sender reputation for independent email streams you send through the shared resources. Consider grouping similar types of customers or email streams into a single tenant or assigning a certain number of customers to each tenant. Although the grouping might be varied, the Amazon SES tenant feature can still minimize the effect of problematic email streams in any one tenant from affecting other tenants by pausing the offending tenant. This helps avoid Amazon SES account-level reputation damage to shared resources, protecting customers and stakeholders using those resources.

Tenant limits and quotas

Amazon SES provides tenant limits to accommodate various organizational scales. The default limit supports 10,000 tenants per account, which is sufficient for most organizations. Qualifying accounts can request increases for more tenants as needed through the Service Quotas console.

Importantly, implementing tenants doesn’t impact your account-level sending quotas or transactions per second (TPS) limits; these remain unchanged and continue to apply across the tenants within your account.

Low-friction adoption

Amazon SES tenants are designed for low-friction adoption, providing isolated containers for your existing Amazon SES email infrastructure. With tenants, you can continue sending emails using your current domains, configuration sets, and IP pools while progressively associating them with appropriate tenants. If you use them, your senders can continue targeting their assigned configuration set. After you’ve configured your Amazon SES account with tenants, instruct your customers or event buses to add a new Amazon SES API call or SMTP header specifying their unique tenant ID. If you aren’t yet using configuration sets, we’ve found that the introduction of the Amazon SES tenants feature often serves as a compelling reason for ISVs and large organizations to adopt configuration sets. If your organization has been looking for ways to offer enhanced email services to existing customers, you might want to use this opportunity to review customer accounts and offer dedicated IPs or email archiving to important email workloads. The ability to gradually roll out the Amazon SES tenants feature facilitates selective adoption, starting with a subset of customers, brands, event buses, or workloads before expanding to your entire customer base. This phased approach enables testing and refinement of your tenant strategy without disrupting existing email operations. Organizations can validate their tenant configuration with low-risk customers before rolling out to critical segments, providing a smooth transition to enhanced reputation management.

Conclusion

In this post, we discussed key aspects of the migration process to Amazon SES tenants, from initial assessment to tenant structure planning. Part 3 of this series will cover details of IAM configuration, monitoring setup, and practical implementation examples. Whether you’re running a SaaS platform, managing multiple brands, operating separate BUs, or just starting to use Amazon SES for your organization, the tenant feature provides simple reputation isolation, boosts efficiency, and helps create better experiences for users.

To learn more, refer to the following resources:


About the authors

Learning email deliverability with Amazon SES

Post Syndicated from Alaa Hammad original https://aws.amazon.com/blogs/messaging-and-targeting/learning-email-deliverability-with-amazon-ses/

Emails landing in spam folders? Facing account suspension warnings? This blog post walks you through seven targeted video tutorials that address the most common Amazon Simple Email Service (Amazon SES) deliverability challenges our customers encounter. Each video in this series provides actionable insights which will allow you to gain knowledge on every aspect of email deliverability, improve your email performance and increase your inbox placement rates.

Why email deliverability matters

Poor deliverability can lead to emails landing in spam folders, increase bounce rates, and can trigger account suspension. Email deliverability directly impacts:

  • Your sender reputation
  • Inbox placement rates
  • Customer engagement
  • Business revenue
  • Compliance with email service provider policies.

Our video series will help you avoid these pitfalls and build a robust email delivery strategy, including onboarding to Amazon SES, choosing your IP, monitoring feedback, reputation, and how to handle unsubscribes, bounces, and complaints.

Onboarding your email solution to Amazon SES

Watch: SES Deliverability learning series: Onboarding your email solution to Amazon SES

What you’ll learn: This video provides a complete migration roadmap for moving your email system to Amazon SES, including domain verification and authentication setup through DKIM, SPF, and DMARC protocols. You’ll learn how to move the account to the production access, acquire and configure dedicated IPs, create and manage configuration sets, and set up Virtual Deliverability Manager for email monitoring. The video also covers the difference between SMTP and API sending methods to explain bulk sender requirements and how to comply with them for successful email delivery.

How this helps with email deliverability: Following SES best practices when onboarding your email system to Amazon SES will help protect your emails from being spoofed, with proactive monitoring using Virtual Deliverability Manager, you can identify issues before they impact delivery, maintaining strong sender reputation and sustained email delivery success.

Choosing the right Amazon SES IP

Watch: SES Deliverability learning series: Choosing the Right Amazon SES IP for Your Email Needs

What you’ll learn: Key differences between shared IPs, dedicated IPs (Managed and Standard), How to choose the right IP environment based on your sending volume and use case, best practices for managing IP reputation, the IP warm-up process and its importance, and shared responsibilities between you and Amazon SES when using dedicated IPs. This video shows you how to rebuild trust with mailbox providers through proper IP management.

How this helps with email deliverability: If your account’s reputation was impacted due to poor sending practices, switching to dedicated IPs can help isolate your reputation and provide better control over your sending environment.

Monitoring email feedback

Watch: Mastering email deliverability: Monitoring email feedback

What you’ll learn: Discover how Amazon SES feedback loops can prevent account pause by alerting you to rising complaint, understanding and analyzing complaint data, utilizing mailbox provider postmaster tools, setting up comprehensive monitoring dashboards, tracking deliverability and engagement metrics, and strategies for emails to reach intended recipients. This video teaches you how to set up systems that alert you to rising complaint rates, bounce rates, and other warning signs before they started to impact your account’s ability to send emails.

How this helps with email deliverability: Proper monitoring is essential for preventing further complaints or bounces that could put your AWS account at risk of termination.

Email reputation with Amazon SES

Watch: Mastering email deliverability: Email Reputation with Amazon SES

What you’ll learn: What email reputation is and why it matters, how email providers evaluate sender reputation, best practices for isolating and managing your reputation, authentication methods to protect your reputation, and effective monitoring techniques for reputation management.

How this helps with email deliverability: Understanding email reputation is important for maintaining a good reputation with mailbox providers.

Handling email unsubscribes

Watch: Mastering email deliverability: Handling email unsubscribes

What you’ll learn: The importance of proper unsubscribe handling, how to remove unsubscribes from your SES mailing lists, the connection between unsubscribes and deliverability rates, strategies to maintain clean email lists, how to identify and prevent mass unsubscribe events, what to do when you see high unsubscribe rates. This video shows you how to properly handle unsubscribes and reduce rates that might trigger spam complaints by mailbox providers or impact your account’s ability to send emails.

How this helps with email deliverability: High unsubscribe rates often indicate content quality issues, sending emails to users who don’t want or expect, list hygiene issue, not having a proper confirmed opt-in, and sending too frequently.

How to handle bounces and boost inbox success

Watch: Mastering Email Deliverability: How to Handle Bounces and Boost Inbox Success

What you’ll learn: Different types of email bounces (soft and hard bounces), What each bounce type indicates about your campaign health, practical strategies to manage and reduce bounce rates, best practices for cleaning your email lists, how to implement effective bounce monitoring systems, steps to identify and address unusual bounce trends, and actionable solutions for common bounce-related challenges. This video provides specific strategies to clean your lists and reduce bounce rates that could impact your account’s ability to send emails, helping you demonstrate improved sending practices with SES.

How this helps with email deliverability: High bounce rates often indicate that a sender is sending unsolicited email to their recipients.

Understanding and handling email complaints

Watch: Mastering Email Deliverability: Understanding and Handling Email Complaints

What you’ll learn: What email complaints are and why they matter, different types of complaints (spam reports, unsubscribe requests), how complaints directly impact your deliverability rates, the correlation between complaint rates and spam folder placement , strategies to keep complaint rates within ideal ranges, practical tips to proactively reduce email complaints, and best practices for content creation and opt-out options. This video helps you understand how to maintain a healthy sender reputation by implementing best practices that align with email service provider guidelines and minimize potential risks to the email system.

How this helps with email deliverability: Mailbox providers may reject emails received from senders based on the complaints they received from their recipients.

Take action today

Don’t let deliverability issues impact your business success. Each video in this series provides practical, actionable guidance that you can implement immediately. Whether you’re dealing with account suspensions issue or simply want to optimize your email performance, these videos will help you establish reliable email delivery that meets mailbox provider requirements.

Ready to get started? Begin with the video that addresses your most pressing challenge, then work through the complete series to learn more about all aspects of email deliverability with Amazon SES.

Have questions about implementing these strategies? The AWS Support team is here to help you optimize your email deliverability and resolve any challenges you may be facing.


About the author

How to register for a US toll-free number with AWS End User Messaging

Post Syndicated from Tyler Holmes original https://aws.amazon.com/blogs/messaging-and-targeting/how-to-register-for-a-us-toll-free-number-with-aws-end-user-messaging/

As businesses increasingly use SMS messaging to engage with customers at scale, having the right origination identity is crucial. Toll-free numbers (TFNs) are the quickest way to begin sending to the United States and offer a trusted, high-visibility option that can drive greater response and brand recognition. This post is for every company that wants to send to the US or internationally.

Obtaining and properly registering a US toll-free number requires a registration process and adhering to requirements set forth by mobile carriers. This comprehensive guide walks you through the step-by-step procedure for registering a US toll-free number through AWS End User Messaging, which provides robust SMS capabilities to AWS customers.

The benefits of using a US toll-free number

TFNs offer several key advantages over other SMS origination types in the US market:

Toll-free facts

  • The opt-out flow for US TFNs is managed at a network level and enforced by US Carriers. If a user sends the word stopor any of the other supported keywords—to the TFN, the carrier sends the following outbound message to the user: NETWORK MSG: You replied with the word "stop" which blocks all texts sent from this number.
    Text back unstop or start to receive messages again. This behavior cannot be changed.
  • Toll-free numbers have a throughput of three Message Parts per Second (MPS).
  • International toll-free numbers are two-way capable in the US and Canada but are one-way only in all other supported countries. Depending on the country being sent to, if not the US or Canada, your end-user can receive your message from an originator other than your TFN. This feature can be turned on before or after registration.

The TFN registration process

To get started, you need to create a US toll-free number registration in the AWS Management Console for AWS End User Messaging or use the API.

  1. Company information: Provide details about your business, including the company name, website, and headquarters address.
  2. Contact information: Enter the name, email, and phone number of the individual who will serve as the main point of contact for your TFN program. This email address should match the domain of the company being registered and cannot be a distribution list, contact group, or mailing list. This information will be used for verification or in the event of something needing to be communicated to you about your TFN. It will not be public knowledge.
  3. Messaging use case: Describe how you intend to use the TFN, including your estimated monthly SMS volume, and select the Use Case Category (such as two-factor authentication, notifications, or marketing).
  4. Use case details: It’s critical that the Use Case Details field and all message templates are consistent with the Use Case Category you selected in the previous step.

For example, if you select two-factor authentication or one-time passwords, your Use Case Details should explain how you plan to use your TFN for that use case, who you will interact with, and why. Answers must be written in English, and it is very important to be clear and concise in this section. Humans are reviewing these, so make sure that everything you write can be understood without prior knowledge of your company or your use case.

  1. Opt-in Workflow Description: This has several boiler-plate components that must be present at the point of opt-in and are discussed in depth in this blog post. If you have a verbal opt-in, you can include the script in this field. If you have a publicly available form, you can supply the URL in the description. Regardless of the format, you must include the following elements at the point of opt in:
    1. Program (brand) name.
    2. Explicitly state the purpose of the SMS program that your end-users are opting into.
    3. Have no prefilled checkboxes, radio buttons, or other fields.
    4. Message frequency disclosure. For example: Message frequency varies or One message per login.
    5. Customer care contact information. For example, Text HELP or call 1-800-111-2222 for support.
    6. Opt-out information. For example: Text STOP to opt-out of future messages.
    7. Include Message and data rates may apply disclosure.
    8. Link to a publicly accessible terms and conditions page.
        • Note: See this post on opt-in processes for terms that must be included.
        • If you are unable to include a public link to your terms, you can include them in the Opt-in workflow image field or alternatively attach them to the registration form or another method like an Amazon S3 presigned URL. Make sure to keep it separate from the actual opt-in screenshots.
    9. Link to a publicly accessible privacy policy page.
        • Note: Carriers are primarily concerned with data sharing of opt-in information to third parties. It’s recommended to have a specific SMS section that addresses that no data gathered during opt-in is shared. See this post on opt-in processes for more details on creating a compliant privacy policy.
        • If you’re unable to include a public link, you can include the full terms in the Opt-in workflow image field or alternatively attach them to the registration form or another method like an Amazon S3 presigned URL. Make sure to keep it separate from the actual opt-in screenshots.
  2. Opt-in workflow image: Upload an image showing how users consent to receiving messages.
    • The maximum file size is 500 KB, and valid file extensions are PDF, JPEG, and PNG.
    • This could be a screenshot of a non-public form, a written consent form, or other evidence of a compliant explicit opt-in that includes all the elements detailed previously.
    • Make sure that the screenshot is clear and readable; degraded image quality will likely be rejected regardless of compliance.
  3. Message samples: Each sample message should reflect actual messages to be sent, should match the Use Case Category you indicated previously, and should follow these best practices:

    • Indicate any variable fields with brackets and make sure to be clear what information can be replaced.
    • Example: Hi, [FirstName] this is AnyCompany letting you know that your delivery is ready.
    • Each sample message must be at least 20 characters. If you plan to use multiple message templates, include them too.
    • Ensure that all messages include your brand name and that it’s consistent with the previously entered information.
    • Make sure your messaging doesn’t involve prohibited content such as cannabis, hate speech, and so on; and that your use case is compliant with AWS Messaging Policy.
  4. Review and submit: Verify that all information is accurate before submitting your registration for approval. There are no exceptions to an explicit opt-in—this includes one-time password use cases, so make sure that your registration includes all the required elements.

The TFN provisioning process

After your TFN registration is submitted it will be reviewed by the same third-party as all other SMS vendors across the globe, not by AWS. You can find current registration time estimates in the number registration process. While waiting, you can monitor your registration status for rejection or acceptance. This AWS blog post has an example of using AWS Lambda to monitor status changes.

If your registration is rejected, the status will change to REQUIRES_UPDATES and should have at least one rejection reason that needs to be reviewed and updated before resubmitting. Follow these instructions to update a rejected registration.

Sending SMS messages and monitoring delivery receipts

After your TFN is activated, you can begin sending SMS messages through AWS End User Messaging. It’s important to monitor your program closely and maintain compliance, because carriers might filter or block your messages if there are issues with your program. This blog post reviews best practices for how to monitor deliverability of SMS messages.

Conclusion

Make sure to follow each step carefully and answer each question completely. There are humans reviewing these so it’s important that your answers are succinct and clear.

As an AWS customer, you have access to powerful messaging capabilities through AWS End User Messaging. By following the steps outlined in this guide, you can quickly register for a US toll-free number to start your SMS outreach. Maintaining compliance is key, and with a TFN in place, you’ll be well on your way to delivering highly effective, compliant SMS messaging that drives real business impact. If you have other questions about AWS End User Messaging, see the comprehensive API specs, the User Guide, or reach out to AWS Support.


About the authors

Track OTP success with AWS End User Messaging SMS feedback

Post Syndicated from Rommel Sunga original https://aws.amazon.com/blogs/messaging-and-targeting/track-otp-success-with-aws-end-user-messaging-sms-feedback/

In this post, we show how to implement message feedback for SMS one-time passwords (OTPs) using AWS End User Messaging. OTP verification through SMS is a fundamental component of modern authentication systems. Although sending OTPs follows an established pattern, tracking their delivery and usage presents several challenges. This post shows how to implement the AWS End User Messaging Message Feedback API to monitor OTP delivery and conversion rates effectively. This post highlights the Message Feedback API in an OTP use case; for practical examples and detailed guidance on building a secure OTP architecture, see Build a Secure One-Time Password Architecture with AWS.

Challenges with OTP tracking

Organizations commonly face these key challenges with OTP tracking:

  • Relying solely on Delivery Receipt (DLR) data for confirming message delivery, which is third-party carrier data that can be subject to interpretation by carriers or message providers, whereas conversion tracking through message feedback provides first-party data that can more accurately reflect actual message delivery and usage
  • Measuring accurate user authentication success rates
  • Identifying OTP verification issues across different geographic regions, carriers and delivery paths

To address these challenges, you can use the AWS End User Messaging Message Feedback API to track delivery and conversion rates, providing first-party data for more accurate insights into message delivery and usage patterns. Although OTP use cases are the most common and serve as our example implementation of message feedback, the same tracking logic can also be applied to other types of SMS conversions, such as promotional link clicks, shopping cart additions, account activations, appointment confirmations, and delivery notifications.

Solution overview

The OTP message flow consists of two main phases. Let’s first examine how the system handles the initial OTP request.

Phase 1: OTP request flow

When a customer initiates an OTP request, your system begins a carefully orchestrated process. First, your application receives this request and generates a unique OTP. With the OTP generated, your system prepares to send it through the AWS End User Messaging API, specifically enabling message feedback tracking by setting the MessageFeedbackEnabled parameter to true when calling SendTextMessage.

Upon successful sending, it returns a unique message ID, which your system must store alongside the generated OTP. This message ID serves as a crucial tracking identifier for the entire verification process. The message is then dispatched to the customer’s device, and your system enters a waiting state, ready to process the verification attempt.

The following diagram illustrates the OTP request flow.

OTP Request Flow Diagram

Phase 2: OTP verification flow

The verification process begins when the customer receives the OTP through SMS and submits it back to your system. Upon receiving the submission, your system first validates the OTP against the stored value. This verification step is critical, because its outcome determines how you will update the message feedback status.

If the customer successfully verifies the OTP, your system calls the PutMessageFeedback API with the stored message ID and sets the status to "RECEIVED", indicating successful delivery and usage of the OTP. However, if the verification fails or the customer doesn’t respond within the timeout period, your system sets the status to "FAILED".

If your system doesn’t explicitly update the feedback status within 1 hour, AWS automatically sets it to "FAILED".

The following diagram illustrates the OTP verification flow.

Prerequisites

Before you begin implementing OTP message feedback, make sure you have the following components and permissions in place:

Send SMS with message feedback enabled

You can enable message feedback in two ways. The first method is to use the MessageFeedbackEnabled parameter when sending an SMS, the second is to send a message with a configuration set with message feedback already enabled. Using a configuration set is often more convenient for bulk implementations because you don’t need to specify message feedback settings in each API call.

To send an SMS with message feedback enabled directly, you can use the following function:

import boto3

# Initialize the End User Messaging client
client = boto3.client('pinpoint-sms-voice-v2')

def send_otp_with_feedback():
    # Generate a unique OTP
    otp = generate_otp()  
    
    # Send SMS with feedback enabled
    response = client.send_text_message(
        DestinationPhoneNumber='+15555550123',  # Replace with your destination phone number
        OriginationIdentity='+14255550120',  # Replace with your origination identity
        MessageBody=f'Your verification code is: {otp}',
        MessageFeedbackEnabled=True
    )
    
    # Store OTP details for verification
    store_otp_details(response['MessageId'], otp)
    return response['MessageId']

The function uses the following details:

  • store_otp_details() is a placeholder function where you store the OTP details in a database for later retrieval
  • generate_otp() is a placeholder function where you generate your OTPs to send using SMS

If you prefer to use a configuration set with message feedback enabled, you can use the following alternative function:

def send_otp_with_feedback_using_configuration_set():
    # Initialize the End User Messaging client
    client = boto3.client('pinpoint-sms-voice-v2')
    
    # Generate OTP
    otp = generate_otp()
    
    # Send SMS using configuration set
    response = client.send_text_message(
        DestinationPhoneNumber='+15555550123',  # Replace with your destination phone number
        OriginationIdentity='pool-201d59fffd554bdfbaf9ee8aEXAMPLE',  # Replace with your origination identity
        MessageBody=f'Your verification code is: {otp}',
        ConfigurationSetName='example-us-east-configuration-set'  # Replace with your configuration set name
    )
    
    # Store OTP details for later verification
    store_otp_details(response['MessageId'], otp)
    
    return response['MessageId']

Your configuration set must have message feedback enabled to use this option. You can enable it using the AWS Command Line Interface (AWS CLI) with the following command:

aws pinpoint-sms-voice-v2 set-default-message-feedback-enabled \
--configuration-set-name "YourConfigSetName" \
--message-feedback-enabled

Another option is to use the AWS End User Messaging console, where you can enable message feedback under Set Settings for the desired configuration set.

Update feedback

After you send a message, you can update the message status to indicate whether a user has successfully completed an action, such as entering the OTP on your application or webpage:

def update_message_feedback(message_id: str, status: str) -> dict:
    try:
        # Initialize the End User Messaging client
        client = boto3.client('pinpoint-sms-voice-v2')
        
        # Update the message feedback status
        response = client.put_message_feedback(
            MessageId=message_id,
            MessageFeedbackStatus=status
        )
        
        return response
        
    except Exception as e:
        print(f"Error updating message feedback: {str(e)}")
        raise

# Example usage
message_id = "a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"  # Replace with your message ID
status = "RECEIVED"  # Use "FAILED" for unsuccessful verifications

result = update_message_feedback(message_id, status)
print(f"Feedback status updated: {result}")

Verify feedback metrics

The AWS End User Messaging dashboard provides comprehensive metrics to help you monitor your OTP performance. The following metrics are available for customizable time periods:

  • Number of messages with feedback completion
  • Percentage of messages with feedback completion
  • Number of SMS with feedback completion by country

To review your application’s overall message feedback metrics, choose Dashboard in the AWS End User Messaging console navigation pane, then choose Message Feedback Metrics.

The dashboard presents three key metrics:

  • Number of messages with feedback completion – The count of SMS and MMS messages where the message feedback record is set to RECEIVED
  • Percentage of messages with feedback completion – The percentage of SMS and MMS messages where the message feedback record is set to RECEIVED
  • Number of SMS with feedback completion by country – The count of message feedback received by country

The progression to 100% completion indicates optimal system performance, where all sent OTPs were successfully received and verified by users, and the message feedback record is set to RECEIVED within the expected timeframe. This high completion rate suggests effective message delivery and a smooth user verification experience. Variations in completion rates across countries can help identify potential regional delivery challenges or user behavior patterns.

The 30% conversion starting point shown in this example is used for illustration purposes only, demonstrating messages that were intentionally left unconverted during testing.

Best practices for OTP implementation

For a secure and reliable OTP implementation, follow these best practices to balance security with user experience:

  • Include rate limiting to prevent abuse
  • Implement proper timeout mechanisms for OTPs
  • Make sure error handling provides clear feedback to users
  • Maintain comprehensive logging for security audits

Conclusion

By implementing the Message Feedback API for OTP tracking, you can gain valuable insights into your authentication system’s effectiveness in real time. This approach helps you monitor successful OTP usage and identify potential delivery issues that might affect user authentication, with granular metrics broken down by geographic regions. The data collected through message feedback offers a more accurate picture of actual user interactions compared to carrier-provided delivery receipts, helping you make data-driven decisions about your authentication system.

To build upon this foundation, consider implementing Amazon CloudWatch alerts for your conversion metrics, and optimizing your message templates based on performance data. The combination of real-time feedback, detailed analytics, and proactive monitoring can help make sure your OTP system remains both secure and efficient.

For additional implementation guidance and best practices, refer to the following resources:


About the authors

A Complete Guide to Resource Sharing for AWS End User Messaging

Post Syndicated from Brett Ezell original https://aws.amazon.com/blogs/messaging-and-targeting/a-complete-guide-to-resource-sharing-for-aws-end-user-messaging/

Introduction

Do you need to send SMS across multiple AWS accounts? Or have you ever wanted to use the same specific 10DLC phone number or branded Sender ID across those accounts? Perhaps your development team needs to test an application in a sandbox account using a production-ready number, or you’re migrating a workload to a new account and need to ensure your customer communications aren’t disrupted. Centralizing your messaging resources across accounts improves efficiency and branding, while lowering the risk in compliance gaps..

In this step-by-step guide, we will show how to solve this challenge by sharing your AWS End User Messaging resources across multiple AWS accounts using AWS Resource Access Manager (AWS RAM). By creating a single sharing account for your messaging resources—like phone numbers, Sender IDs, and opt-out lists—and securely sharing them with your other “consuming” accounts, you can build a more efficient, secure, and scalable communication platform.

Common Use Cases for Resource Sharing

Important: resource sharing with AWS RAM is a regional feature. You can only share resources with accounts within the same AWS Region where those resources are located.

Centralizing and sharing resources is a powerful pattern that addresses several common customer needs:

  • Testing in a Sandbox Environment: Allows development teams to test applications using production-ready phone numbers or Sender IDs in an isolated sandbox account, without giving them access to production configurations.
  • Simplified Registration and Onboarding: Share an existing pre-registered 10DLC number or Sender ID with a new account that has not yet completed its own registration process, enabling it to start sending messages more quickly.
  • Seamless Account Transitions: When migrating an application or workload to a new AWS account, you can share the existing origination identities. This makes certain that your phone numbers and Sender IDs remain consistent during the transition, preventing any disruption to your customer-facing communications.

This guide will walk you through the step-by-step process of sharing your AWS End User Messaging resources.

Shareable AWS End User Messaging Resources

You can share the following AWS End User Messaging resources using AWS RAM:

  • Phone Numbers: Share your dedicated short codes, 10DLCs, long codes, and toll-free numbers. This allows different accounts to send messages using a centralized pool of numbers.
  • Sender IDs: Share alphanumeric sender IDs to maintain consistent branding in one-way SMS messages across your accounts.
  • Opt-out Lists: Centralize your opt-out management to ensure regulatory compliance. When a user opts out of messaging from one account, they are opted out across all accounts using that shared list. This is especially powerful when used with pools, as you can associate a pool with a specific opt-out list, ensuring all numbers in that pool adhere to the same primary list. As a best practice, you should create and share a dedicated opt-out list rather than relying on the default list for each account.
  • Pools: Share your pools of phone numbers and sender IDs to manage origination identities at scale. Pools provide benefits like automatic failover and apply settings like opt-out lists or two-way SMS configurations to the entire pool.
    • Important: for a shared Opt-out list or pool to be functional, all of its member resources (the phone numbers and/or Sender IDs within it) must also be included in the same AWS RAM resource share.

Understanding AWS RAM Fundamentals

Before sharing your End User Messaging resources, it’s essential to understand the core concepts of AWS RAM.

  • Resource Share: This is the central component in AWS RAM. A resource share consists of three elements:
    • The resources to be shared (such as phone numbers, or opt-out lists).
    • The principals (AWS accounts, OUs, or an entire organization) with whom you are sharing.
    • The managed permissions that define what actions the principals can perform on the shared resources.

Important: The supported resources of AWS End User Messaging are shareable with AWS accounts, Organizations, and OUs, but not with individual AWS Identity and Access Management (IAM) roles or users. This restriction ensures that resource sharing remains at the account level, maintaining clear boundaries and simplifying access management for your End User Messaging infrastructure.

  • Sharing Account vs. Consuming Account:
    • The sharing account (or owner account) is the AWS account that owns the resources and creates the resource share.
    • When a principal (such as an AWS account) is granted access to a resource share, it becomes a consuming account. It can use the shared resources according to the permissions granted and pays for its own usage of those resources, not for the resources themselves. For example: The consuming account pays for the volume of SMS sent by a shared number but the sharing account pays for any fees associated with owning that actual number.
  • AWS Organizations Integration: While you can share resources with individual AWS accounts, the most powerful way to use AWS RAM is in conjunction with AWS Organizations. This service allows you to centrally manage and govern multiple AWS accounts under a single umbrella. When you enable sharing within your organization, you can share resources with all accounts in the organization, or with specific Organizational Units (OUs), seamlessly and without needing to send and accept individual invitations. This sharing is only possible between accounts that reside in the same AWS Region.
  • Managed Permissions: AWS RAM uses managed permissions to control access.
    • AWS managed permissions are predefined permission sets created and maintained by AWS for common use cases. For AWS End User Messaging, the key permission is AWSRAMDefaultPermissionSmsVoice, which allows consumers to use the resources for sending messages but not for deleting or modifying them.
    • Customer managed permissions can be created for more granular control over shared resources.
  • Resource-Based Policies: Behind the scenes, AWS RAM works by creating and managing resource-based policies for you. These policies are what actually grant the consuming accounts access to the shared resources.

To better illustrate these sharing models, the following diagrams show how a Sharing Account can share its AWS End User Messaging resources using different strategies:

Diagram 1: Direct Account-to-Account Sharing:

Diagram 2: Sharing with an Entire AWS Organization:

Diagram 3: Sharing with a Specific Organizational Unit (OU):

Prerequisites and Setup

For the following walkthrough, we will demonstrate how to configure the setup for Diagram 1: Direct Account-to-Account Sharing. However, the steps for managing and using the resource share are similar for all three scenarios. Before you begin, ensure your environment is set up correctly.

Note for AWS Organizations Users: When your account is managed by AWS Organizations, you can take advantage of that to share resources more easily. With or without Organizations, a user can share with individual accounts. However, if your account is in an organization, then you can share with individual accounts, or with all accounts in the organization or in an OU without having to enumerate each account.

If you plan to share resources using AWS Organizations (as shown in Diagram 2 or Diagram 3), you must complete the following prerequisite steps from your organization’s management account before creating a resource share:

1. Enable all features in your organization:

aws organizations enable-all-features

2. Enable resource sharing with AWS RAM: This creates the necessary service-linked role.

aws ram enable-sharing-with-aws-organization

1. Required IAM Permissions

The IAM user or role performing these actions needs permissions for both AWS RAM and AWS End User Messaging. The following policy grants the necessary permissions to manage resource shares.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "RAMResourceShareManagement",
            "Effect": "Allow",
            "Action": [
                "ram:UpdateResourceShare",
                "ram:DeleteResourceShare",
                "ram:AssociateResourceShare",
                "ram:DisassociateResourceShare"
            ],
            "Resource": "arn:aws:ram:*:*:resource-share/*"
        },
        {
            "Sid": "DiscoveryAndCreationPermissions",
            "Effect": "Allow",
            "Action": [
                "ram:CreateResourceShare",
                "ram:GetResourceShares",
                "ram:ListResources",
                "organizations:ListAccounts",
                "organizations:DescribeOrganization",
                "pinpoint-sms-voice-v2:DescribePhoneNumbers",
                "pinpoint-sms-voice-v2:DescribeSenderIds",
                "pinpoint-sms-voice-v2:DescribeOptOutLists",
                "pinpoint-sms-voice-v2:DescribePools"
            ],
            "Resource": "*"
        }
    ]
}

Note on Least Privilege: This policy follows the security best practice of granting least privilege. The first statement scopes modification permissions to only AWS RAM resource shares. The second statement grants permissions for discovery actions (like Describe* and List*) and the ram:CreateResourceShare action, which require "Resource": "*" as they do not operate on a specific, pre-existing resource.

2. Regionality Requirement

Important Reminder: resource sharing with AWS RAM is a regional feature. You can only share resources with accounts within the same AWS Region where those resources are located.

For example, a resource in us-east-1 can only be shared with other accounts in us-east-1, regardless of where those accounts operate other resources. Ensure that the resources you intend to share and the accounts that you anticipate sharing with are each considering the same Region for this process.

Creating and Managing Resource Shares (Sharing Account Actions)

This section provides a step-by-step guide to sharing your resources using the AWS CLI. We will walk through creating a resource share, associating and disassociating resources, and checking the status of your shares.

Step 1: Create an Empty Resource Share

First, create the resource share. Think of this as an empty container. You will associate principals (the consuming accounts) and resources (the phone numbers, etc.) with this share.

In the command below, we will create a share named EUM-Shared-Resources for an external account.

# Create a resource share and grant default End User Messaging permissions # Replace 123456789012 with the consuming account's ID
aws ram create-resource-share \
    --name "EUM-Shared-Resources" \
    --principals "123456789012" \
    --permission-arns "arn:aws:ram::aws:permission/AWSRAMDefaultPermissionSmsVoice" \
    --allow-external-principals \
    --region us-east-1
  • --principals: Specify one or more AWS account IDs.
  • --allow-external-principals: This flag is required when sharing with accounts that are not part of your AWS Organization.

Expected Response: A successful command returns a JSON object describing the new resource share. Note that allowExternalPrincipals is now true.

{
    "resourceShare": {
        "resourceShareArn": "arn:aws:ram:us-east-1:111122223333:resource-share/a1b2c3d4-5678-90ab-cdef-example11111",
        "name": "EUM-Shared-Resources",
        "owningAccountId": "111122223333",
        "allowExternalPrincipals": false,
        "status": "ACTIVE",
        "tags": [],
        "featureSet": "STANDARD"
    }
}

For the following sections and when specifying resource ARNs, ensure you’re using the correct format for AWS End User Messaging resources:

  • Phone numbers: arn:aws:sms-voice:region:account-id:phone-number/phonenumber-id
  • Sender IDs: arn:aws:sms-voice:region:account-id:sender-id/senderid
  • Opt-out lists: arn:aws:sms-voice:region:account-id:opt-out-list/optoutlist-id
  • Pools: arn:aws:sms-voice:region:account-id:pool/pool-id

Replace ‘region‘, ‘account-id‘, and the specific resource IDs with your actual values.

Step 2: Associate Resources with the Share

Now that you have your “container,” you can add resources to it. The associate-resource-share command links one or more of your End User Messaging resources to the share you just created, making them available to the principals.

# Define the ARN of the resource share from the previous step
RESOURCE_SHARE_ARN="arn:aws:ram:us-east-1:111122223333:resource-share/a1b2c3d4-5678-90ab-cdef-111111111111"

# Associate a phone number and a pool with the share # Replace the resource-arns with your actual resource ARNs
aws ram associate-resource-share \
    --resource-share-arn "$RESOURCE_SHARE_ARN" \
    --resource-arns \
        "arn:aws:sms-voice:us-east-1:111122223333:phone-number/phonenumber-a1b2c3d4" \
        "arn:aws:sms-voice:us-east-1:111122223333:pool/pool-b2c3d4e5" \
    --region us-east-1

Expected Response: A successful association returns a JSON object confirming the association and showing its status. The status will initially be ASSOCIATING and will transition to ASSOCIATED once complete.

Note: The association process is asynchronous. We’ll show you how to verify the completion status in the next step using the get-resource-shares and list-resources commands. It’s important to confirm the status has changed to ASSOCIATED before attempting to use the shared resources.

Step 3: Verify the Status and contents of the Share

Before making changes, it’s good practice to verify what’s in the share. Use get-resource-shares to check the status and list-resources to see the contents. This process helps ensure that all intended resources are properly associated and accessible to the principals you’ve designated.

# Verify the association status is ASSOCIATED
aws ram get-resource-shares \
    --resource-owner SELF \
    --name "EUM-Shared-Resources" \
    --association-status ASSOCIATED \
    --region us-east-1

Expected Response: If the command returns no results, wait a few moments and try again. The association process is typically quick but can sometimes take up to a few minutes.

{
    "resourceShares": [
        {
            "resourceShareArn": "arn:aws:ram:us-east-1:111122223333:resource-share/12345678-abcd-1234-efgh-111122223333",
            "name": "EUM-Shared-Resources",
            "owningAccountId": "111122223333",
            "allowExternalPrincipals": true,
            "status": "ACTIVE",
            "creationTime": "2023-07-01T12:00:00.000Z",
            "lastUpdatedTime": "2023-07-01T12:00:00.000Z",
            "featureSet": "STANDARD"
        }
    ]
}

Review the output carefully to ensure all intended resources are listed. If any resources are missing, you may need to reassociate them using the associate-resource-share command.

Expected Response (list-resources): This command will return a list of JSON objects, each representing a resource in the share.

# List the ARNs of all resources currently in the share
aws ram list-resources \
    --resource-owner SELF \
    --resource-share-arns "$RESOURCE_SHARE_ARN" \
    --region us-east-1

Review the output carefully to ensure all intended resources are listed. If any resources are missing, you may need to reassociate them using the associate-resource-share command.

# List the ARNs of all resources currently in the share
aws ram list-resources \
    --resource-owner SELF \
    --resource-share-arns "$RESOURCE_SHARE_ARN" \
    --region us-east-1

Expected Response (list-resources): This command will return a list of JSON objects, each representing a resource in the share.

{
    "resources": [
        {
            "arn": "arn:aws:sms-voice:us-east-1:111122223333:phone-number/phonenumber-a1b2c3d4",
            "type": "sms-voice:PhoneNumber",
            "resourceShareArn": "arn:aws:ram:us-east-1:111122223333:resource-share/a1b2c3d4-5678-90ab-cdef-example11111",
            "status": "AVAILABLE"
        },
        {
            "arn": "arn:aws:sms-voice:us-east-1:111122223333:pool/pool-b2c3d4e5",
            "type": "sms-voice:Pool",
            "resourceShareArn": "arn:aws:ram:us-east-1:111122223333:resource-share/a1b2c3d4-5678-90ab-cdef-example11111",
            "status": "AVAILABLE"
        }
    ]
}

Step 4: Disassociate Specific Resources from the Share

To stop sharing a specific resource, you use the disassociate-resource-share command. You must provide the ARN of the resource you wish to remove. This gives you granular control, allowing you to remove one resource while continuing to share others.

# Disassociate only the phone number from the share
aws ram disassociate-resource-share \
    --resource-share-arn "$RESOURCE_SHARE_ARN" \
    --resource-arns "arn:aws:sms-voice:us-east-1:111122223333:phone-number/phonenumber-a1b2c3d4" \
    --region us-east-1

Expected Response: The response will be nearly identical to the associate response, confirming the disassociation request. The status will be DISASSOCIATING.

{
    "resourceShareAssociations": [
        {
            "resourceShareArn": "arn:aws:ram:us-east-1:111122223333:resource-share/a1b2c3d4-5678-90ab-cdef-example11111",
            "associatedEntity": "arn:aws:sms-voice:us-east-1:111122223333:phone-number/phonenumber-a1b2c3d4",
            "associationType": "RESOURCE",
            "status": "DISASSOCIATING",
            "external": false
        }
    ]
}

How to Use Shared Resources

Once resources are shared, users in the consuming accounts can discover and use them for sending messages.

Step 1: Discovering Shared Resources

From a consuming account, you can list resources that have been shared with you by using the --filters parameter in the describe-* commands.

Note: Shared resources are discoverable via the AWS CLI and SDKs but will not appear in the AWS Management Console of the consuming account. This is expected behavior, as the resources are owned by the sharing account.

# List phone numbers shared with your account
aws pinpoint-sms-voice-v2 describe-phone-numbers \
    --filters Name=shared-with-me,Values=true \
    --region us-east-1
# List sender IDs shared with your account
aws pinpoint-sms-voice-v2 describe-sender-ids \
--filters Name=shared-with-me,Values=true \
--region us-east-1

# List pools shared with your account
aws pinpoint-sms-voice-v2 describe-pools \
--filters Name=shared-with-me,Values=true \
--region us-east-1

# List shared opt-out lists with region specification
aws pinpoint-sms-voice-v2 describe-opt-out-lists \
--filters Name=shared-with-me,Values=true \
--region us-east-1

Expected Response: The command returns a JSON object listing the shared resources, including their ARNs, which you will need for sending messages.

{
    "PhoneNumbers": [
        {
            "PhoneNumberArn": "arn:aws:sms-voice:us-east-1:111122223333:phone-number/phonenumber-a1b2c3d4",
            "PhoneNumberId": "phonenumber-a1b2c3d4",
            "PhoneNumber": "+12065550100",
            "Status": "ACTIVE",
            "MessageType": "TRANSACTIONAL",
            "TwoWayEnabled": true,
            "CreatedTimestamp": "2023-10-26T14:34:56.123Z"
        }
    ]
}

Step 2: Sending Messages with Shared Resources

Important: When using shared resources, consuming accounts must specify the full ARN of the shared resource in API calls. This differs from resource owners, who can use either the resource ID, ARN, or the number directly. You can specify the ARN of an individual phone number or a pool as the origination-identity.

# Send an SMS using a shared Phone Number ARN (consuming account MUST use ARN)
aws pinpoint-sms-voice-v2 send-text-message \
    --destination-phone-number "+12065550199" \
    --origination-identity "arn:aws:sms-voice:us-east-1:111122223333:phone-number/phonenumber-a1b2c3d4" \
    --message-body "Hello from a shared number!" \
    --region us-east-1

# Send an SMS using a shared Pool ARN (consuming account MUST use ARN)
aws pinpoint-sms-voice-v2 send-text-message \
    --destination-phone-number "+12065550199" \
    --origination-identity "arn:aws:sms-voice:us-east-1:111122223333:pool/pool-b2c3d4e5" \
    --message-body "Hello from a shared pool!" \
    --region us-east-1

Expected Response: A successful send-text-message call will return a MessageId, which confirms that the service has accepted the message for delivery.

{
    "MessageId": "a1b2c3d4-5678-90ab-cdef-example22222"
}

Message Delivery Reporting:

Once a message is sent, understanding its delivery status is crucial for ensuring your communications are effective. AWS End User Messaging provides several mechanisms for tracking message delivery, giving you a multi-layered approach to reporting.

Delivery Receipts (DLRs):

For traditional, carrier-provided Delivery Receipts (DLRs), which can sometimes take up to 72 hours to be returned, you must configure an event destination. This is the most common method for confirming that a message has reached the recipient’s handset, and is achieved through a Configuration Set.

For shared resources:

  • The configuration set must be created and managed in the sharing account.
  • The consuming account must then reference the ARN of the configuration set when sending messages.
# Example for consuming account
aws pinpoint-sms-voice-v2 send-text-message 
    --destination-phone-number "+12065550199" 
    --origination-identity "arn:aws:sms-voice:us-east-1:111122223333:phone-number/phonenumber-a1b2c3d4" 
    --message-body "Hello from a shared number!" 
    --configuration-set-name "arn:aws:sms-voice:us-east-1:111122223333:configuration-set/MyConfigSet" 
    --region us-east-1

For a detailed walkthrough, see our companion blog post, How to Send SMS Using Configuration Sets with AWS End User Messaging.

Message Feedback:

For more immediate, application-driven insights, you can use the Message Feedback feature. This allows you to programmatically mark messages as “delivered” based on a user’s action, such as using a one-time password (OTP) or clicking a link in the message. This provides a real-time confirmation loop that is independent of carrier DLRs.

Amazon CloudWatch:

To monitor these events at scale, you can stream them to Amazon CloudWatch Logs to track key performance indicators like the number of messages sent and delivered, and to set up alerts based on your specific business needs.

To set up comprehensive reporting:

  1. Configure an event destination for DLRs and detailed status events.
  2. Set up CloudWatch dashboards and alerts for ongoing monitoring.

This multi-layered approach provides both immediate feedback and long-term delivery insights, allowing you to optimize your messaging strategy and quickly identify potential delivery issues.

Troubleshooting Common Issues

  • Permission Denied Errors: If a consuming account cannot access a shared resource, verify that the consuming account’s IAM policies include the necessary permissions. Here’s an example policy:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "pinpoint-sms-voice-v2:SendTextMessage",
                "pinpoint-sms-voice-v2:SendVoiceMessage",
                "pinpoint-sms-voice-v2:DescribePhoneNumbers",
                "pinpoint-sms-voice-v2:DescribeSenderIds",
                "pinpoint-sms-voice-v2:DescribeOptOutLists",
                "pinpoint-sms-voice-v2:DescribePools"
            ],
            "Resource": "*"
        }
    ]
}
  • Resource Not Visible: Remember that shared resources do not appear in the consuming account’s AWS Management Console. If the describe-* commands with the shared-with-me filter return no results, ensure the resource share status is ACTIVE in the sharing account.
    • If sharing via AWS Organizations, confirm the consuming account is correctly placed in the specified OU. You can find more information on managing OUs in the AWS Organizations User Guide.
  • CLI Command Fails: If a command fails with a “not found” or “invalid parameter” error, it is often due to an incorrect ARN. Double-check that the ARNs for resources, principals, and the resource share itself are correct. A Permission Denied error, on the other hand, points to an IAM policy issue..

Best Practices and Considerations

  • Security: Always follow the principle of least privilege. Use AWS managed permissions like AWSRAMDefaultPermissionSmsVoice where possible and create customer-managed permissions only for specific, granular requirements.
  • Cost: The sharing account is billed for provisioning the resources (e.g., the monthly cost of a phone number). Consuming accounts are billed for their usage of those shared resources (e.g., the cost per message sent). There are no additional costs for using AWS RAM.
  • Throughput and Quotas: Resource throughput quotas (e.g., messages per second) are shared along with the resource. High volume sending from multiple consuming accounts using the same shared number or pool, could collectively hit the service quota, which may result in throttling. Plan your usage accordingly or request quota increases if necessary.

Conclusion

This guide has equipped you to centralize your AWS End User Messaging resources using AWS Resource Access Manager. By implementing this strategy, you can directly address the common challenges of a multi-account environment: maintaining consistent branding with shared Sender IDs, ensuring comprehensive compliance with centralized opt-out lists, and reducing operational overhead by managing resources in one place.

We have walked through the entire lifecycle, from the initial prerequisites in AWS Organizations and IAM, to the step-by-step CLI commands for creating shares, associating resources, and enabling consuming accounts to use them. By applying these techniques and keeping the best practices for security and throughput in mind, you are now able to build a more efficient, secure, and scalable communication platform across your entire AWS ecosystem.