Back to blog
FILE 0x44·HOW TO SURVIVE AWS TOLL-FREE SMS REGISTRATION

How to survive AWS toll-free SMS registration

May 11, 2026 · aws, sms, compliance

A toll-free SMS number for a personal safety project I run took several denial cycles before AWS approved it. The denials were all about wording in the opt-in fields — nothing about my actual business or use case. Writing down what finally worked, mostly so future-me doesn't have to relearn it.

What was happening

Toll-free SMS in the US requires verification through the carrier ecosystem. AWS End User Messaging packages this as a "registration" with about 22 fields covering company info, contact info, message samples, and use case details. Once submitted, the carriers review and either approve or deny with a reason.

Several denials came back with variations on:

What I found

A few non-obvious mechanics, written down because the docs don't surface them well:

Company name must match the EIN exactly. I had been submitting Frazier Industries Mach 2, LLC (with a comma). The EIN registration is Frazier Industries Mach 2 LLC (no comma). Carriers cross-check this. The comma was an instant denial. Removing it was the single biggest unblocker.

Creating a new registration version produces a blank draft. This one is brutal. When you call create-registration-version to apply fixes after a denial, the new version does NOT carry forward any of the field values from the previous version. You have to re-populate all 22 fields with put-registration-field-value before submitting. The denial reasons usually only relate to two or three fields, but you still have to write all 22. If you forget one, the submission fails or comes back denied for missing data.

URL fields require --cli-input-json, not --text-value. Anywhere a field accepts a URL, the shell quoting on https:// eats things. Use the JSON input flag:

aws pinpoint-sms-voice-v2 put-registration-field-value \
  --cli-input-json '{
    "RegistrationId": "registration-...",
    "FieldPath": "companyInfo.website",
    "TextValue": "https://example.com"
  }'

Opt-in description winning language. What finally cleared review was content that explicitly called out four things: this is for SMS (not email), what the SMS contains, the expected frequency, and a non-condition-of-purchase clause. Concretely:

"By submitting this form you consent to receive SMS messages from the service. You will receive up to 5 SMS messages per month when a monitored event occurs (missed check-in, SOS activation). Consent is for SMS only and is not a condition of purchase."

Plus the opt-in image (a screenshot of the consent form) is a required attachment — messagingUseCase.optInImage field set to the attachment id returned by create-registration-attachment.

The fix (the workflow)

Once you've been denied, the survival pattern is:

# 1. Read denial reasons
aws pinpoint-sms-voice-v2 describe-registration-versions \
  --registration-id registration-...

# 2. Read existing field values from the latest version
#    so you have a reference for what to re-populate
aws pinpoint-sms-voice-v2 describe-registration-field-values \
  --registration-id registration-... \
  --version-number <LATEST>

# 3. Create new draft (blank!)
aws pinpoint-sms-voice-v2 create-registration-version \
  --registration-id registration-...

# 4. Re-populate ALL 22 fields, applying fixes
for each field: put-registration-field-value ...

# 5. Submit
aws pinpoint-sms-voice-v2 submit-registration-version \
  --registration-id registration-...

I scripted this so each retry is one command. Without the script every retry was an hour of clicking through the console and forgetting one field.

What I'd do differently

Approach the whole thing as a compliance exercise, not a configuration exercise. The carriers don't care about your clever architecture. They care that opt-in language is unambiguous, that the company info matches government records exactly, that the frequency claim is realistic.

Also: don't pay extra to keep retrying a denied registration on a different provider in parallel. I burned time on a parallel toll-free verification on a different vendor that also kept getting denied for the same reasons. The reasons would have been the same on every carrier. Fix the wording once, submit once, wait.

After approval, the monitor script I'd been running every 12 hours to detect status drift could be retired. Not everything needs perpetual monitoring once the state actually stabilizes.