AWS Messaging Blog
Migrate production SMS traffic to a short code with AWS End User Messaging
Your new short code just went active, and you’re ready to migrate production SMS traffic to it from a toll-free number or 10-digit long code (10DLC) where you have collected two years of opt-outs. Those recipients still expect to stay opted out. Your STOP and HELP keywords, along with any custom keywords, must work on the new number from the first message. Carriers also treat the short code as a brand-new sender, with no record of the consent history attached to your old number. A misstep can result in messages blocked at the carrier level or, worse, messages delivered to recipients who believe they already unsubscribed.
This post walks through the operational mechanics of cutting over production SMS traffic to a short code using AWS End User Messaging SMS. It applies whether you’re migrating from a toll-free number, a 10DLC, or consolidating multiple originators into a single pool. It covers opt-out list migration, keyword handoff, carrier behavior while both originators are active, and validation steps to verify recipients are not disrupted.
Prerequisites
Before starting the migration, verify the following:
- You send SMS through AWS End User Messaging SMS, using the SendTextMessage API in the pinpoint-sms-voice-v2 namespace.
- Your new short code is in Active status in the AWS End User Messaging SMS console.
- You have completed the short code registration and the carriers have approved it. Short code provisioning can take several weeks to months depending on carrier review, so plan accordingly.
- You have AWS Identity and Access Management (IAM) permissions for the pinpoint-sms-voice-v2 API actions used in this post: DescribePhoneNumbers, DescribeOptedOutNumbers, CreateOptOutList, PutOptedOutNumber, UpdatePhoneNumber, UpdatePool, DescribeOptOutLists, DescribeKeywords, PutKeyword, CreateConfigurationSet, CreateEventDestination, CreatePool, AssociateOriginationIdentity, DisassociateOriginationIdentity, SendTextMessage, ReleasePhoneNumber, and DeleteOptOutList. The export and backfill scripts also need s3:PutObject and s3:GetObject on the Amazon Simple Storage Service (Amazon S3) bucket that holds the export, and creating the event destination requires iam:PassRole for the Amazon CloudWatch Logs role.
- The AWS Command Line Interface (AWS CLI) version 2 and jq are installed. Both are available in AWS CloudShell.
- The new short code is not yet associated with a pool. The UpdatePhoneNumber API returns an error if the phone number is already in a pool. Complete all per-number configuration (opt-out list, keywords, two-way messaging) before adding the short code to any pool.
- If you use self-managed opt-outs, your application already handles STOP and HELP responses and logs opt-out state externally.
The migration surface area
Three data structures travel with an origination identity in AWS End User Messaging SMS, and all three must be accounted for during a migration:
Opt-out lists. Each origination identity is associated with one opt-out list containing phone numbers that have sent STOP (or an equivalent keyword) to that originator. When you provision a phone number, AWS End User Messaging SMS assigns it to the account’s Default opt-out list, so in many accounts every originator shares that single list. If you created custom opt-out lists to separate opt-outs by use case, you need to associate the new originator with the same list.
Keyword configurations. Keywords like STOP, HELP, UNSTOP, and any custom keywords you defined (for example, JOIN or PROMO) each carry an action (OPT_OUT, OPT_IN, or AUTOMATIC_RESPONSE) and a response message. These are configured per origination identity or per phone pool.
Two-way messaging configuration. If your application processes inbound messages through an Amazon Simple Notification Service (Amazon SNS) topic, the new originator must be wired to the same (or equivalent) topic before cutover.
Migrating opt-out lists
The opt-out list is the most critical piece of the migration. Sending a message to someone who previously opted out is a compliance violation in most jurisdictions, and US carriers impose fines for it.
Start by confirming which opt-out list your current originator and your new short code are associated with. The following command returns both in one call. The --phone-number-ids parameter takes the phone number ID or Amazon Resource Name (ARN), not the E.164 number. You can find the ID in the AWS End User Messaging SMS console or by running describe-phone-numbers without parameters.
The result determines your migration path:
- Both originators use the Default list. Opt-outs are already shared. A recipient who opted out on the old number is also blocked on the new short code, so no backfill is needed. Run the export in the next step to create a baseline for validation, then skip ahead to Replicating keyword configurations section.
- The old originator uses a custom list. Associate the short code with the same list, or create a dedicated list and backfill it. Both options are described later in this section.
- You want the short code on its own list. Create a dedicated list and backfill it from whichever list the old originator uses today.
If self-managed opt-outs are turned on for your old originator, AWS End User Messaging SMS doesn’t add STOP replies to the opt-out list automatically. In that case, your application’s opt-out records are the source of truth. Import them into the list associated with the short code before cutover.
Next, export every opted-out number from the opt-out list associated with your current origination identity. If you are using the Default opt-out list, invoke DescribeOptedOutNumbers with the opt-out list name set to Default. For custom opt-out lists, specify the list name you associated with your toll-free or 10DLC number.
If the response contains a NextToken, the list has more entries. Paginate until NextToken is no longer present. The following script automates the full export:
The script writes to a temporary file and then uploads it to Amazon S3. This keeps the export durable regardless of where you run the command (a local workstation, an Amazon Elastic Compute Cloud (Amazon EC2) instance, or AWS CloudShell). You will reference this file during the backfill step.
The --cli-read-timeout flag raises the per-request read timeout above the 60-second default, which helps over slow connections, and adaptive retry mode keeps a long export running through brief throttling.
If your old originator uses a custom list, or you want the short code on its own list, choose one of the following approaches.
Sharing the same opt-out list
If your old and new originators serve the same use case and you want a single source of truth, associate the new short code with the same custom opt-out list your old number used. Use UpdatePhoneNumber to set the OptOutListName on the new short code:
This approach means both originators honor the same opt-out entries during the transition period while you run them in parallel.
Important: The UpdatePhoneNumber API returns an error if the phone number is already associated with a pool. You must set the opt-out list on the short code before adding it to any pool. If the short code is already in a pool, use UpdatePool with the --opt-out-list-name parameter instead.
Creating a dedicated opt-out list and backfilling
If you want the new short code to have its own opt-out list (recommended when you plan to decommission the old number entirely), create a new list and import the exported numbers:
Then import each opted-out number:
Production opt-out lists often hold 100,000 numbers or more, so plan the backfill as a batch job. PutOptedOutNumber has a default quota of 10 requests per second (RPS), and DescribeOptedOutNumbers, which the export uses, has a default of 1 RPS. At those defaults, exporting 100,000 numbers takes at least 17 minutes (1,000 pages of 100 results). Importing them takes at least 2 hours and 45 minutes, and longer in practice because each AWS CLI call adds startup time.
The following script reads the export from Amazon S3 and imports numbers in batches of 10. It runs each batch in parallel, waits for the batch to finish, and then sleeps for one second before starting the next one, which keeps the import at or below the default quota. Numbers that still fail after retries are written to a file so you can rerun them.
The AWS_RETRY_MODE and AWS_MAX_ATTEMPTS settings tell the AWS CLI to back off and retry when a request is throttled, instead of stopping at the first ThrottlingException. If you raise BATCH_SIZE after a quota increase, keep the one-second sleep so each batch stays within your approved rate. When the import finishes, rerun any numbers in failed-numbers.txt, then compare the count against your export as described in Validating the migration section.
If the backfill window is too long for your migration plan, request a higher rate for PutOptedOutNumber and DescribeOptedOutNumbers. Every action in the pinpoint-sms-voice-v2 API is eligible for a rate increase through the Service Quotas console or an AWS Support case, as described in Quotas for AWS End User Messaging SMS. AWS Support provides an initial response within 24 hours, so submit the request several days before your migration window.
After backfilling, associate the new list with the short code using UpdatePhoneNumber (before the short code is added to a pool).
One important nuance: Nothing outside your AWS opt-out list carries over to the short code. For US toll-free numbers, carriers also block STOP senders at the network level, but that block is tied to the specific toll-free number and does not transfer. Opt-outs recorded against a 10DLC campaign likewise stay with that campaign. From the carrier’s perspective the short code is a new sender, so the opt-out list associated with it, whether shared with your old number or backfilled from your export, is the only mechanism that honors prior opt-out requests.
Replicating keyword configurations
Keywords define how your originator responds to inbound messages. If a recipient texts HELP to your current toll-free number and receives a support message, the same behavior must exist on the new short code before any traffic shifts.
Export the keyword configuration from your existing originator:
For each keyword returned, replicate it on the new short code with the same action and response message:
Repeat for HELP, any OPT_IN keywords (like START or UNSTOP), and custom AUTOMATIC_RESPONSE keywords. Verify each one by invoking DescribeKeywords on the new originator and comparing the output against your source configuration.
If your existing originator also routes inbound messages to an Amazon SNS topic for two-way processing, configure the same on the new short code:
For self-managed opt-out configurations where your application handles STOP and HELP logic rather than the service, turn on that setting for the new originator as well. Two-way messaging must be turned on before you can turn on self-managed opt-outs:
With self-managed opt-outs enabled, AWS End User Messaging SMS stops automatically processing STOP and HELP keywords. Your application receives these messages through the SNS topic and must handle opt-out logic, respond to the sender, and maintain compliance records.
Order of operations. UpdatePhoneNumber returns an error after a phone number is associated with a pool, and CreatePool copies the short code’s keywords, opt-out list, two-way settings, and self-managed opt-out setting into the new pool. Complete the per-number configuration on the short code in this order:
- Set the opt-out list (UpdatePhoneNumber
--opt-out-list-name). Skip this step if both originators use the Default opt-out list. - Replicate keywords (PutKeyword).
- Turn on two-way messaging (UpdatePhoneNumber
--two-way-enabled). - Turn on self-managed opt-outs, if you use them (UpdatePhoneNumber
--self-managed-opt-outs-enabled). This step requires two-way messaging from step 3. - Add the short code to a pool (CreatePool with the short code as the origination identity, or AssociateOriginationIdentity for an existing pool).
If you need to change a setting after step 5, use UpdatePool to change the pool-level configuration instead.
Carrier behavior during transitions
Carriers treat each originator type as an independent entity. This is the operational reality that makes originator migrations more complex than a configuration change.
Toll-free to short code. US carriers manage toll-free opt-outs at the carrier network level. When a recipient sends STOP to your toll-free number, the carrier blocks future messages from that specific toll-free number. This block does not carry over to your short code. From the carrier perspective, the short code is an entirely new sender. Your responsibility is to honor prior opt-outs through the short code’s opt-out list, either by sharing the list your toll-free number uses or by importing its entries, even though the carrier won’t enforce them automatically.
10DLC to short code. 10DLC numbers and short codes go through separate registration processes. Your 10DLC campaign is registered with The Campaign Registry (TCR). A US short code goes through its own brand vetting by the US Short Code Registry and approval by each carrier. Moving traffic to the short code doesn’t change your 10DLC campaign. The campaign stays active, and the old number stays associated with it, until you release the number and delete the campaign as described in Cleaning up section. Opt-outs recorded against your 10DLC campaign also don’t carry over to the short code. The opt-out list associated with your short code, whether shared with your old number or backfilled from your export, is the authoritative source of truth.
Behavior while both originators are active. During the transition window when both your old and new originators are sending to the same recipient pool, a recipient might receive messages from both numbers in the same day. As a general practice, avoid sending identical content from both originators to the same recipient, because carriers might interpret this as duplicate messaging.
Grace period considerations. There is no industry-standard grace period for originator transitions. After you stop sending from the old originator, recipients can no longer text STOP to it (since the number will eventually be decommissioned or reassigned). This creates a gap: a recipient who wants to opt out during the transition might not know which number to text. The mitigation is to communicate the number change proactively. Send a one-time notification from the old originator informing recipients that future messages will come from the new short code, and include opt-out instructions for the new number.
Configuring delivery event monitoring
Before any production traffic reaches the short code, set up a configuration set with an event destination so you can compare delivery results between your old and new originators. AWS End User Messaging SMS sends basic events to Amazon EventBridge by default, but those events omit the recipient phone number and identify the sender by phone number ID, which makes side-by-side comparison and alarming difficult. A configuration set with an event destination streams full event records to Amazon CloudWatch Logs, Amazon Data Firehose, or Amazon SNS.
If you already send with a configuration set that has an event destination, keep using it for both originators and skip to the query later in this section. Otherwise, create a configuration set and a CloudWatch Logs event destination. The event destination requires an existing log group and an IAM role that allows AWS End User Messaging SMS to write to it, as described in Set up an Amazon CloudWatch event destination.
Configuration sets apply per message, not per originator. Pass --configuration-set-name on every SendTextMessage request during the transition, whether the request goes to the old number, the short code, or a pool that contains both. Messages sent without it don’t reach the event destination.
Each event record includes an originationPhoneNumber field, so a single configuration set covers both originators. To compare them, run the following query in CloudWatch Logs Insights against your log group:
Run the query once before cutover to record a baseline for the old originator, then again throughout the transition window.
Cutover strategy
A hard cutover (switching 100% of traffic instantly) introduces risk you can avoid. Two approaches work well for phased migrations.
Pool-based routing. Create a phone pool containing both your old originator and the new short code. When you send through a pool, AWS End User Messaging SMS selects an originator from the pool. Start with a 48- to 72-hour validation window where both originators serve traffic. Monitor delivery events in the log group you configured in Configuring delivery event monitoring section, comparing delivery rates and carrier failure events per originator, and track new opt-outs in your opt-out list. After the short code’s delivery success rate is consistent with the old number’s rate, begin the final cutover by removing the old originator from the pool.
When creating the pool, understand that the pool inherits its configuration (keywords, message type, opt-out list, two-way settings, and self-managed opt-out configuration) from the origination identity you specify in the CreatePool request. Deletion protection isn’t inherited and defaults to false regardless of the source originator’s setting. Because this pool carries production traffic, turn on deletion protection explicitly when you create the pool, or later with UpdatePool. If you want the short code’s configuration to be the pool default, use it as the origination identity when creating the pool, then associate the old number afterward using AssociateOriginationIdentity.
Set --message-type to the message type of your short code.
Three constraints apply:
- Configuration match required. Every phone number you add to a pool must have the same configuration as the first number specified when the pool was created, and AssociateOriginationIdentity returns an error if it doesn’t. For example, if the pool was created with a number that has two-way messaging enabled, the other number must also have two-way messaging enabled before you can add it to the pool. The same applies to opt-out lists: both numbers must use the same list. If you created a dedicated opt-out list for the short code, either use application-layer routing during the parallel run, or keep both numbers on the shared list while they are in the pool and switch the pool to the dedicated list with UpdatePool after you remove the old number.
- Matching message type. The pool’s message type (TRANSACTIONAL or PROMOTIONAL) comes from the number you create it with and can’t be changed after the pool is created. You can’t add a TRANSACTIONAL number to a pool created from a PROMOTIONAL number, or the reverse. A phone number’s message type is set when you request the number, and UpdatePhoneNumber doesn’t change it. If your old number and short code have different message types, use application-layer routing instead.
- One pool per phone number. A phone number can only be associated with one pool at a time. If your old originator is already in a different pool, you must disassociate it first using DisassociateOriginationIdentity.
Application-layer routing. If pool-based routing does not fit your architecture (for example, if you need deterministic control over which recipients see which originator), implement the split at the application layer. Route a percentage of recipients to the new short code using a feature flag or weighted distribution. Increase the percentage gradually over several days as delivery metrics hold. This gives you granular control, and you can roll back instantly by setting the weight back to zero.
Rollback. If delivery rates on the short code drop significantly below the old originator’s baseline, or you observe a spike in TEXT_CARRIER_UNREACHABLE events, revert immediately. For pool-based routing, remove the short code from the pool. For application-layer routing, set the short code weight to zero. Investigate the root cause before resuming.
Timing. Plan the cutover during business hours on a weekday when your team and carrier support are available, in case issues arise that require investigation.
Whichever approach you choose, send the recipient transition notification from the old originator before beginning the cutover. In this message, tell recipients that future messages will arrive from the new short code number, and remind them they can reply STOP at any time.
Validating the migration
Before decommissioning the old originator, verify four things.
Opt-out list completeness. Compare the count of entries in the new opt-out list against your exported baseline. Paginate through DescribeOptedOutNumbers for both the original export and the new list, and compare the totals. If you backfilled a dedicated list, the numbers should match exactly. If you shared a list between both originators, verify that new opt-outs received during the transition period are being recorded.
Keyword responses. Send test messages containing each keyword (STOP, HELP, custom keywords) to the new short code from a test handset. Verify that the response messages match your expected content and that the opt-out or opt-in actions fire correctly in your opt-out list.
Inbound message routing. Send a non-keyword message to the new short code and verify it arrives at your SNS topic and is processed by your application. This validates the two-way messaging path end to end.
Delivery metrics. Compare delivery success rates on the new short code against the old originator over your transition window. Use the event destination configured on your configuration set to track per-originator delivery events in Amazon CloudWatch. Watch for elevated failure rates that might indicate a carrier-side issue with the short code registration that needs resolution before you complete the cutover.
Cleaning up
After the migration is validated and the old originator is no longer receiving traffic:
- Remove the old origination identity from any phone pools using DisassociateOriginationIdentity.
- If the old number was a toll-free or 10DLC you no longer need, release it through the AWS End User Messaging SMS console or by using the ReleasePhoneNumber API to stop incurring monthly lease charges. Releasing a number is permanent and removes your rollback path, so wait until the short code has carried production traffic through your validation window.
- If you moved off a 10DLC number, delete the 10DLC campaign after every number associated with it is released. The campaign’s recurring registration fee continues until you delete it. You can delete the campaign under Registrations in the AWS End User Messaging SMS console, as described in Delete a registration. Deleted campaigns can’t be restored. If other 10DLC numbers still send under the campaign, keep it.
- If the old originator used a custom opt-out list that no other originator or pool references, delete it with DeleteOptOutList. Keep the Default opt-out list, which every account requires.
- Update any IAM policies, alarm configurations, or configuration sets that referenced the old originator’s ARN.
Conclusion
In this post, we showed how to migrate production SMS traffic from a toll-free number or 10DLC to a short code using AWS End User Messaging SMS. The critical areas are opt-out list portability, keyword configuration replication, carrier-level opt-out isolation between originator types, and a phased cutover with rollback readiness.
Carriers treat your short code as a brand-new sender, so carrier-level STOP blocks on a toll-free number and opt-outs recorded against a 10DLC campaign stay with the old number. The opt-out list associated with your short code is the only record that protects recipients who already unsubscribed. By confirming which opt-out list each originator uses, migrating consent records where needed, validating behavior on the new originator, and monitoring delivery metrics against a defined baseline before completing the cutover, you reduce the risk of compliance violations and delivery disruptions.
To begin, run the opt-out list check in Migrating opt-out lists section against your current originator and your short code, and set up the configuration set described in “Configuring delivery event monitoring” so you have a baseline before cutover. If your short code is still being provisioned, see Getting your SMS short code production-ready with AWS End User Messaging. For consent requirements and registration planning, see A Guide to SMS Short Codes with AWS End User Messaging. If you send from multiple AWS accounts and want them to share the short code, a pool, or an opt-out list, see A Complete Guide to Resource Sharing for AWS End User Messaging. For the full API reference, see the AWS End User Messaging SMS and Voice v2 API documentation, or visit the AWS End User Messaging SMS detail page.