Verification codes are the texts users notice when they fail. If a password reset code arrives four minutes late, or never arrives, users file support tickets and some abandon checkout. Most of these failures don't come from the API call. They come from registration: the wrong use case, a number shared with promo traffic, or a team that assumed the US works like the last country they shipped in.

This post covers what a product team needs before sending OTP and notification texts to US numbers. It explains how US 10DLC registration differs from template regimes like India's DLT, and how to keep login codes separate from marketing so that marketing traffic can't slow them down. We make Ready, so the worked examples use our pricing and API. The registration rules apply whichever provider you use.

What the US actually requires for OTP traffic

If you send application-to-person (A2P) texts from a regular 10-digit US number, carriers expect that traffic to be registered under 10DLC. Registration has two layers:

  1. Brand. This is your company: legal name, EIN, website and contact details.
  2. Campaign. This describes one kind of traffic. You choose a use case, describe how people opt in, and submit sample messages.

Verification codes usually go under a 2FA or account-notification style use case. Order updates, appointment alerts and similar texts are notifications. Promotions are marketing. The use case you pick affects how carriers review and treat the traffic. The details are in matching use case to what you actually send.

10DLC is not a statute. It is a carrier requirement, and carriers enforce it by filtering unregistered traffic. If you want the legal background, see is 10DLC a law and who must register. For an OTP flow, "filtered" means codes silently fail to arrive, which is the outcome you are trying to avoid.

What registration costs on Ready

  • No setup fee. At submission you pay the first month: $30.
  • After that, the monthly cost is $25/mo for registration, plus $2 per number.
  • Most approvals take 2 to 7 days.

Ready runs the brand and campaign registration inside the app. Platforms that register on behalf of their own customers can do it per sub-account through the API, with status available at GET /v1/registration. Details are in the quickstart.

How this differs from India's DLT (and similar regimes)

Teams that have shipped OTP in India often carry over the wrong assumptions. The two systems differ in what gets registered.

India's DLT system, under TRAI's commercial communications regulations, broadly requires the sending entity to register on an operator-run DLT platform. The entity also registers its sender headers and its message templates, which are the exact text with variable slots marked. If a message doesn't match an approved template, it can be blocked. Traffic is also split into transactional, service and promotional categories, each handled differently.

US 10DLC does not lock you to exact templates. You submit sample messages that represent the campaign, but you don't pre-approve every string you will ever send. You can rephrase "Your code is 482913" as "482913 is your Acme login code" without filing anything. The constraint in the US is use case: the content has to match what you registered. If you send a discount offer through a campaign registered for 2FA, you are outside your registration, even if the wording is fine.

US 10DLCIndia DLT (broadly)
What's registeredBrand + campaign (use case, opt-in flow, samples)Entity, headers, exact content templates
Wording changesAllowed if they stay within the use caseNew wording generally needs a matching template
Sender identityYour 10-digit number(s) on the campaignRegistered alphanumeric header
Main failure modeFiltering/throttling for mismatched or unregistered trafficBlocking for template mismatch

On scope: Ready is built around US carrier registration. We don't run DLT registration for India-bound traffic. If you need India delivery, you need a route that handles DLT. Other countries with sender-ID or template rules need the same kind of check.

Keep OTP physically separate from marketing

This decision has the biggest effect on OTP reliability. Give OTP its own campaign and its own numbers. Don't run login codes through the same campaign or number that sends your weekly promo.

Separation matters for three reasons:

  • Throughput is metered per campaign. A fresh standard campaign without vetting gets roughly 1 to 4 segments per second. If a 20,000-recipient promo is draining that queue, a password reset waits behind it.
  • Opt-outs follow numbers and contacts. If a user texts STOP to the number that sends your promos, and your codes come from that same number, you have tied their ability to log in to their marketing preference. Ready honors STOP across campaigns, so an opted-out number is refused up front and not charged. That is correct behavior, but your product has to handle the refusal. We cover the UX side in designing send-error UX around pre-send refusals.
  • Complaints are attributed to traffic. Spam reports against marketing shouldn't count against the campaign that carries your login codes.

The extra cost is one campaign at $20/mo plus $2 per number. For most products, that is small compared to the support load of codes that don't arrive.

When a single mixed campaign is fine

A mixed use case is a real option, and for a small product it can be enough. If you send a few hundred codes a month and an occasional product update, one mixed campaign is simpler to manage. Split the traffic once marketing volume could reasonably compete with codes for throughput. For more on sender setup and deliverability specific to codes, see bulk SMS for OTP and verification.

Writing OTP messages that stay at one segment

Every segment is billed, so code texts should stay short and plain.

  • Fits in one segment: Your Acme code is 482913. It expires in 10 minutes. Don't share it. This is 67 characters of GSM-7 text, well under the 160-character single-segment limit.
  • What breaks it: a single emoji switches the message to unicode, which lowers the limit to 70 characters. Adding a 🔒 switches the message to unicode, so it now has no room left: one more character and it becomes 2 segments. Ready swaps curly quotes, long dashes and ellipses for plain characters before sending, so those don't change the count. Emoji still do.

Name your brand in the message so users recognize the sender. Keep the opt-in path consistent with what you registered.

Worked example: 50,000 codes a month

Assumptions: one 70-character GSM-7 code per verification, 50,000 sends a month, and one OTP campaign with one number.

  • Sends: 50,000 × 1 segment × $0.0075 = $375 at the Standard rate, plus 50,000 × $0.0045 in carrier fee.
  • Registration: $25/mo for registration and $2/mo per number.
  • Replies: if users reply to codes, those inbound texts are billed like sent texts.

Ready's all-in prices per segment include the carrier fee but not screening: Scrub lists and Number validation, the two switches in Settings under Compliance, each add $0.003 per segment when switched on, shown as screening on your bill, and both are off by default. Unless you turn the opt-out footer off, the first text to each contact, and one again from time to time, also gets "Reply STOP to opt out" and your business name if the message does not already have them, so a text close to the segment limit can bill one more segment.

To be fair on price: Twilio lists around $0.0083 per segment, below our $0.0075 Standard rate. Purpose-built verification products from CPaaS providers are also a reasonable choice if all you need is codes. Ready is worth paying for when you want registration handled per sub-account, opt-out and quiet-hours enforcement built in, and one API covering OTP, notifications and two-way conversations.

Throughput and daily caps for alert spikes

Notification traffic is spiky. A shipping batch or an outage alert can send thousands of texts in a few minutes.

At an assumed 3 segments per second on a fresh standard campaign, 5,000 single-segment alerts take about 28 minutes to drain. That's fine for "your order shipped." It's a problem if a login code is queued behind them, which is one more reason to keep OTP on its own campaign.

T-Mobile also applies a daily brand cap based on trust score, roughly 2,000 messages/day at the lowest tier and stepping up through about 10,000, 40,000, 100,000 and 200,000. Brand vetting ($79 Standard / $179 Enhanced, one-time) can raise the score. It only moves your daily cap if the new score crosses a tier, and it doesn't change the per-second rate. Most low-volume senders don't need it.

Compliance details that affect OTP specifically

Consent. A user who types their number into your signup form and requests a code has asked for that text. In the API, consent_attested: true is your promise that the recipient agreed. Ready records the attestation per send: who sent, to how many, and when. It does not store per-contact opt-in wording or page URLs. Your app should keep that record. (A contact who signs up on a Ready-hosted opt-in page is the exception: Ready stores that sign-up, with the time, IP address and exact wording, and support@tryready.com can send you a copy.)

Quiet hours. Ready enforces permitted local hours for the recipient's area: an API text is held only if the window opens within 6 hours, otherwise it is refused and you send it again. You are charged only when the text actually sends. For marketing, holding is what you want. For a code someone requests at 11:40 PM, a held message is useless. Check how quiet hours apply to your account in the docs, or ask us, before you route time-critical codes through it.

Number validation. GET /v1/validate/:phone checks a number before you depend on it. That helps on signup forms, where people enter office landlines. Numbers Ready has already seen as landlines are refused up front on every account, at no charge. With landline validation switched on ($0.003 per segment, off by default), a send-time lookup refuses more of them.

Abuse protection. Every code request is a send from your balance, so a bot that floods your "send me a code" form with numbers it picks (SMS pumping) costs you money, or harasses someone else's phone. Limit code requests per phone number and per IP (for example 3 per number per 10 minutes and 10 per IP per hour), put a CAPTCHA in front of the first send, refuse numbers that GET /v1/validate/:phone rejects, and alert on a spike in code requests.

Idempotency. Retries are where duplicate codes come from. Send an Idempotency-Key with every request:

POST https://api.tryready.com/v1/messages
X-API-Key: <key>
Idempotency-Key: 6f1c2a9e-3b7d-4e2a-9c51-0d8e7a4b2f13
Content-Type: application/json

{"to": "+12125550199", "body": "Your Acme code is 482913. It expires in 10 minutes. Don't share it.", "consent_attested": true}

You can add from to pin the OTP campaign's number, so codes never go out from your marketing number. Full reference: messages API docs.

The practical checklist

  1. Register a brand and a dedicated OTP/notification campaign with the correct use case.
  2. Put marketing on its own campaign and numbers.
  3. Keep codes GSM-7, under 160 characters, with no emoji.
  4. Send an idempotency key and pin from on every code.
  5. Handle opt-out refusals in your UX, and confirm how quiet hours affect late-night codes.
  6. If you send outside the US, confirm the destination's rules (DLT for India) with a route that supports them.

To test the flow, sign up and use your first 20 free one-to-one texts before you register. To send, you add a card and get a number, which is $2 a month with the first month charged when you get it. Once registration is submitted the free texts end, and most approvals take 2 to 7 days, so start that early.