AWS Messaging & Targeting Blog

Automating Sender ID Configuration for SMS with AWS End User Messaging APIs

Global SMS messaging with consistent Sender ID branding requires configuring the same Sender ID across multiple countries, which is a time-consuming process for businesses operating internationally. In this post, we’ll show you how to automate the configuration of Sender IDs for countries that do not have registration requirements using the AWS End User Messaging v2 API.

The Challenge of Multi-Country Sender ID Configuration

When sending SMS messages internationally, if you are using Sender ID, you want your brand to be consistently recognized. This means configuring the same Sender ID in each country you send to. However there are several challenges related to this:

  • Each country must be done manually, one at a time, if using the AWS Console
  • If you have multiple environments for testing the process must be repeated for each Account/Region
  • If you are moving account/regions you have to reconfigure each country in the new Account/Region
  • Manual configuration can be error prone
  • Errors can be detrimental to a brand

The Solution: AWS Lambda Automation

This solution can be applied to any country that supports Sender ID configurations and does not require registration. You can view a comprehensive list of these eligible countries here.

Below is an AWS Lambda function that streamlines this process by handling bulk configuration requests. The function solves the challenges in handling multiple country configurations in a single automated workflow. Here’s the complete code:

import boto3
import uuid
import json
import time
from typing import Dict, Any, List

def validate_input(sender_id: str, countries: List[str]) -> bool:
    if not 1 <= len(sender_id) <= 11:
        raise ValueError("Sender ID must be between 1 and 11 characters")
    
    if not sender_id.replace('_', '').replace('-', '').isalnum():
        raise ValueError("Sender ID can only contain alphanumeric characters, underscore, and hyphen")
    
    if not countries:
        raise ValueError("At least one country code must be provided")
    
    return True

def request_sender_id(client, sender_id: str, countries: List[str], message_types: List[str] = None, tags: List[Dict] = None) -> List[Dict]:
    if message_types is None:
        message_types = ["TRANSACTIONAL", "PROMOTIONAL"]
    
    results = []
    
    for i, country in enumerate(countries):
        if i > 0:
            time.sleep(1)  # Rate limiting: 1 request per second
            
        try:
            print(f"Processing country: {country} ({i+1}/{len(countries)})")
            request_params = {
                'ClientToken': str(uuid.uuid4()),
                'SenderId': sender_id,
                'IsoCountryCode': country.upper(),
                'MessageTypes': message_types,
                'DeletionProtectionEnabled':True
            }
            
            if tags:
                request_params['Tags'] = tags
                
            response = client.request_sender_id(**request_params)
            
            results.append({
                'Country': country,
                'Status': 'Success',
                'SenderIdArn': response.get('SenderIdArn'),
                'MonthlyLeasingPrice': response.get('MonthlyLeasingPrice')
            })
            
        except Exception as e:
            results.append({
                'Country': country,
                'Status': 'Failed',
                'Error': str(e)
            })
            
        print(f"Completed {country}: {'Success' if results[-1]['Status'] == 'Success' else 'Failed'}")
    
    return results

def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
    try:
        # Extract parameters from event
        sender_id = event['sender_id']
        countries = [c.strip().upper() for c in event['countries'] if c.strip()]
        tags = event.get('tags')  # Optional
        message_types = event.get('message_types', ["TRANSACTIONAL", "PROMOTIONAL"])  # Optional
        
        # Validate input
        validate_input(sender_id, countries)
        
        # Initialize the AWS SMS Voice v2 client
        client = boto3.client('pinpoint-sms-voice-v2')
        
        # Process the request
        results = request_sender_id(
            client=client,
            sender_id=sender_id,
            countries=countries,
            message_types=message_types,
            tags=tags
        )
        
        # Calculate summary
        successful = sum(1 for r in results if r['Status'] == 'Success')
        
        return {
            'statusCode': 200,
            'body': {
                'results': results,
                'summary': {
                    'total': len(countries),
                    'successful': successful,
                    'failed': len(countries) - successful
                }
            }
        }
        
    except Exception as e:
        return {
            'statusCode': 500,
            'body': {
                'error': str(e)
            }
        }

How the Lambda Function Works to Configure Sender IDs

The Lambda function automates the Sender ID configuration process through these key steps:

  • Input Validation: Ensures the Sender ID meets format requirements (1-11 alphanumeric characters, with optional underscores or hyphens).
  • Bulk Registration: Processes each country sequentially with built-in rate limiting (1 request per second) to prevent API throttling.
  • Logging: Returns the full ARN of each successfully configured Sender ID
  • Flexible Configuration Settings:
    • Supports multiple message types (transactional, promotional, or both)
    • Enables resource tagging for organizational and cost tracking
    • Provides deletion protection to prevent accidental removal
  • Cost Transparency: Displays monthly leasing price for each successful country configuration
  • Error Handling: Individual country failures don’t halt the entire process, allowing partial success if a single country were to fail for some reason.

Using the Lambda Function

To use this function, create a Lambda with the code above and configure an event. The tags are optional and we have provided an example below:
NOTE: Depending on the number of countries you are attempting to register at a time, you may need to increase the 3 second default Lambda timeout. For reference, in our testing, we were able to do all Sender IDs(160) in less than 3 minutes.

Test Event Example

{
    "sender_id": "YourBrand",
    "countries": ["GB", "DE", "FR"],
    "tags": [
        {
            "Key": "Environment",
            "Value": "Production"
        },
        {
            "Key": "Department",
            "Value": "Marketing"
        }
    ]
}

IAM Permissions

Your Lambda will need these minimum AWS Identity and Access Management (IAM) permissions, scale back the resource if necessary:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "sms-voice:RequestSenderId",
                "sms-voice:TagResource"
            ],
            "Resource": "arn:aws:sms-voice:*:**ACCOUNT#**:sender-id/*"
        }
    ]
}

The Trust Policy required for Lambda to assume the role:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "lambda.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

Results Example

The Lambda will return results for each country, including configuration status and monthly costs, if any. Below you will see an example of 3 countries being configured:


{
  "statusCode": 200,
  "body": {
    "results": [
      {
        "Country": "GB",
        "Status": "Success",
        "SenderIdArn": "arn:aws:sms-voice:us-west-2:**ACCOUNT#**:sender-id/LAMBDATEST2/GB",
        "MonthlyLeasingPrice": "0.00"
      },
      {
        "Country": "DE",
        "Status": "Success",
        "SenderIdArn": "arn:aws:sms-voice:us-west-2:**ACCOUNT#**:sender-id/LAMBDATEST2/DE",
        "MonthlyLeasingPrice": "0.00"
      },
      {
        "Country": "FR",
        "Status": "Success",
        "SenderIdArn": "arn:aws:sms-voice:us-west-2:**ACCOUNT#**:sender-id/LAMBDATEST2/FR",
        "MonthlyLeasingPrice": "0.00"
      }
    ],
    "summary": {
      "total": 3,
      "successful": 3,
      "failed": 0
    }
  }
}
Function Logs:
START RequestId: 81d2d144-9023-413d-b976-c87c79a82eae Version: $LATEST
Processing country: GB (1/3)
Completed GB: Success
Processing country: DE (2/3)
Completed DE: Success
Processing country: FR (3/3)
Completed FR: Success

After processing your Sender ID configurations, it’s crucial to verify them. You can do this via the AWS console or by using the DescribeSenderIds API. This API offers flexible retrieval options:

  • Specify individual Sender IDs for targeted information
    • Providing an invalid Sender ID will result in an error
  • Apply filters to narrow your results
  • Retrieve all Sender IDs associated with your AWS account by omitting both

Conclusion

Automating Sender ID configuration with the AWS End User Messaging v2 API and a Lambda function will dramatically reduce the time spent on manual configuration, ensure consistent branding across all supported and configured countries, and simplify a complex process. You’ll gain a scalable, reliable solution that allows you to deploy and manage your Sender IDs with confidence.

Additional resources:

AWS End User Messaging SMS documentation

SMS V2 API

Tyler Holmes

Tyler Holmes

Tyler is a Senior Specialist Solutions Architect. He has a wealth of experience in the communications space as a consultant, an SA, a practitioner, and leader at all levels from Startup to Fortune 500. He has spent over 14 years in sales, marketing, and service operations, working for agencies, consulting firms, and brands, building teams and increasing revenue.