OTP SMS Delivery Best Practices in India
One code, five hops from your server to a phone: who owns each one, what TRAI’s rules check there and what you can see. It makes no delivery-rate promise.
- Hops, Request To Verify
- 5
- Longest Code Life, NIST
- 10 min
- Digits Minimum, NIST
- 6
Checked against TRAI’s regulation, consolidated 21 May 2026, and NIST SP 800-63B-4
- NotifyTech AI Team
- 7 min read
- Development
What an OTP asks of the SMS path
TRAI defines an OTP as an automatically generated random number used to authenticate the action of a user for a single transaction or session. Someone is waiting on a screen for it, so an OTP that arrives late is a login or payment that did not happen.
This guide follows one code from your server to a phone in five hops, says who owns each hop and what TRAI requires there, and ends with a ledger of what to do and what to avoid. The registration steps themselves are in our TRAI DLT registration guide. This is not legal advice: check your DLT portal for your own account.
An OTP at a glance
- Message type
- Transactional (-T): a reply to a customer-initiated transaction within 30 minutes
- Explicit consent
- Not required for a transactional message
- Sender
- A registered header; check its format on your DLT portal
- Links
- None needed. Any link must be whitelisted
- The code
- At least 6 digits, used once, invalid after 10 minutes
What happens between your server and the phone?
Five hops, in order: your request, the template check, the operator network, the handset and your verify step. You own the first and the last. For the three in between, what you can know is limited to what the delivery report tells you.
Request
Seen
Template
Seen
Network
Blind
Handset
Blind
Verify
Seen
Seen: you get an event or a log Blind: nothing reports here
Seen
Your request
Owner: Your app and your provider’s API
What happens
Your server asks the API to send one code to one number, naming the registered header and the registered template.
What you can see
The API’s reply to your request.
The rule
The request carries the sender ID, entity ID and template ID. Without registered values the next hop has nothing to match.
Source: Developer page SMS sample
Seen
The template check
Owner: The DLT platform and your access provider
What happens
The sender, header, wording, links and declared chain are checked against what you registered.
What you can see
A failed event with an error, if the message does not pass.
The rule
Registered headers only. The fixed part of the text must match its template. A link that is not whitelisted is not permitted.
Source: TCCCPR regulation 3 and Schedule I; TRAI Direction, 28 Oct 2024
Blind
The operator network
Owner: The access providers and the telemarketers in your declared chain
What happens
The message is carried to the recipient’s operator. It passes through at most one aggregator and one delivery telemarketer.
What you can see
Nothing until the delivery report arrives.
The rule
A message on a chain that is not declared, or does not match, is rejected.
Source: TCCCPR regulation 22; TRAI Press Release 92/2024
Blind
The handset
Owner: The phone and the person holding it
What happens
The phone has to be on, in coverage, and able to show the message.
What you can see
A delivered event reports a status. It does not say the person saw the code.
The rule
This hop is outside the checks above. Nothing you send can make the phone reachable.
Source: No rule applies
Seen
Your verify step
Owner: Your app
What happens
The person types the code and you check it.
What you can see
Your own logs. This is the only proof that the code reached a person.
The rule
Valid once, invalid after 10 minutes, at least 6 digits, wrong guesses limited.
Source: NIST SP 800-63B-4
You own the first and the last hop. For the three in between, what you can know is what the delivery report tells you, so design the last hop to cope when nothing arrives.
What must be true before an OTP is sent?
TRAI’s regulation turns into a few checks on every OTP: a registered header, a template that matches the text, no promotion mixed in, a whitelisted link if there is one, and a declared chain. An OTP needs the first three and normally no link at all.
Send from a registered header
Commercial SMS may only use registered headers. Register the header, and read its format, including the -T suffix for transactional messages, on your DLT portal.
Register the OTP wording as a template
The fixed part of a template is hashed so each message can be checked against it before and after delivery. Mark the code and its validity period as variables. A template has three variables by default, and belongs to one header.
Keep promotion out of the message
If promotional content is mixed into a transactional message, the whole message is treated as promotional. A thank-you line with an offer is enough.
Leave links out
An OTP needs no link. Traffic containing a URL, APK, OTT link or call-back number that is not whitelisted has not been permitted since 1 October 2024.
Know your declared chain
A message passes through at most one aggregator and one delivery telemarketer, declared on the DLT platform. Since December 2024, SMS sent through an unregistered path is rejected, so change the declaration before you change the route.
Self-certify every year
Registration details, headers and templates that are not certified each year are suspended automatically, and a suspended template stops your OTPs.
How should the code itself be designed?
NIST’s guidance for a code sent over SMS is short: random, at least 6 digits, used once, invalid after 10 minutes, with limits on wrong guesses. A code that expires too soon turns a slow SMS into a failure, and one that lives too long is a risk.
Random, 6 digits or more
NIST asks for a random secret of at least 6 decimal digits, from an approved random generator.
Invalid after 10 minutes
NIST treats the authentication as invalid unless it is completed within 10 minutes. TRAI’s thirty minutes decides the message type; it is not a validity period.
Valid once
A code that has been used, or replaced by a newer one, must stop working. Resending means issuing a new code, not sending the old one again.
Limit wrong guesses
NIST requires rate limiting on consecutive failed attempts, and says generating a new code must not reset the failure count. Count failures against the account or number, not the code.
SMS is a restricted channel
NIST marks authentication over the phone network as restricted. Weigh risk signals such as a SIM change or a ported number, tell people about the limitation, and always offer an alternative that is not SMS.
What do delivery reports tell you?
Your provider reports each message’s status to a webhook you control. Ours sends message.delivered and message.failed. They tell you what happened to the message, not whether the person read it, so your verify step stays the only proof that the code arrived.
import requests
def send_sms():
url = "https://api.notifytechai.com/v1/sms/send"
headers = {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
}
payload = {
"to": "+919876543210",
"message": "Your OTP is 123456",
"sender_id": "YOUR_SENDER_ID",
"dlt_entity_id": "1234567890123456",
"dlt_template_id": "1234567890123456789"
}
response = requests.post(
url, json=payload, headers=headers
)
return response.json()
result = send_sms()
print(result)<?php
// webhook.php - Handle delivery reports
$payload = file_get_contents('php://input');
$data = json_decode($payload, true);
$signature =
$_SERVER['HTTP_X_NOTIFYTECH_SIGNATURE'];
$secret = 'your_webhook_secret';
if (!verifySignature($payload, $signature, $secret)) {
http_response_code(401);
exit('Invalid signature');
}
switch ($data['event']) {
case 'message.delivered':
updateMessageStatus(
$data['message_id'], 'delivered'
);
break;
case 'message.failed':
updateMessageStatus(
$data['message_id'], 'failed'
);
logError($data['error']);
break;
}
http_response_code(200);
echo json_encode(['status' => 'processed']);These are the samples from the developer page. The code, the IDs and the secret are placeholders, and your template decides the text.
Verify the signature header before you update anything. The full list of events, fields and keys is on the developer page and in the documentation. Set your own alert levels from your own baseline: a figure from someone else’s traffic is not yours.
What should you do, and what should you avoid?
The same advice as one ledger. Every “don’t” is a rule from TRAI, NIST or Meta that appears above, not a rule of thumb.
Don’t
Don’t: Put a link in the OTP
Do instead
Do: Send the code, a validity period and a warning, nothing else
The rule
A link that is not whitelisted is not permitted.
Source: TRAI Direction, 28 Oct 2024
Don’t
Don’t: Add an offer or a thank-you line
Do instead
Do: Keep the text transactional from end to end
The rule
Promotional content mixed in makes the whole message promotional.
Source: TCCCPR regulation 2
Don’t
Don’t: Change the wording without registering it
Do instead
Do: Register the new text as its own template first
The rule
The fixed part of each message is checked against its registered template.
Source: TCCCPR Schedule I
Don’t
Don’t: Resend the same code, or accept an old one
Do instead
Do: Issue a new code and retire the earlier one
The rule
A code is valid once and for 10 minutes at most.
Source: NIST SP 800-63B-4
Don’t
Don’t: Reset the wrong-guess count on a resend
Do instead
Do: Count failures per account or number
The rule
A new code must not reset the failure count.
Source: NIST SP 800-63B-4
Don’t
Don’t: Treat “sent” as “received”
Do instead
Do: Wait for the delivery event, and let the person ask again
The rule
A status event is not proof that someone read the code.
Source: Developer page webhook sample
Don’t
Don’t: Switch routes in a hurry
Do instead
Do: Declare the new chain first, then send
The rule
A chain that is not defined or does not match is rejected.
Source: TRAI Press Release 92/2024
Don’t
Don’t: Rely on SMS alone
Do instead
Do: Offer a second way to sign in
The rule
NIST restricts the phone network as an authenticator.
Source: NIST SP 800-63B-4
Rules read from TRAI’s regulation, NIST and Meta on 20 October 2026. They say nothing about delivery times: no source does.
Frequently Asked Questions
Common questions about OTP SMS delivery in India
References & Further Reading
The regulator’s and the standard’s own documents behind this guide
- Read the Regulations
TCCCPR 2018, consolidated
TRAI’s Telecom Commercial Communications Customer Preference Regulations as consolidated on 21 May 2026: the definitions of an OTP and a transactional message, headers, templates and scrubbing. The gazetted text prevails.
- Read the Direction
TRAI Direction, 28 October 2024
Whitelisting of URLs, APKs, OTT links and call-back numbers, and rejection of messages whose telemarketer chain is not defined or does not match.
- Read the Release
SMS traceability, Press Release 92/2024
TRAI’s note of 19 December 2024: traffic sent through unregistered paths is rejected from 11 December 2024.
- Read the Guideline
NIST SP 800-63B-4
Digital identity guidelines for authentication: out-of-band codes, their length and lifetime, rate limiting, and the restricted status of the phone network.
- Read the Documentation
WhatsApp authentication templates
Meta’s documentation for one-time-code templates: preset body text, the optional expiry line and the code-delivery buttons.
Send OTPs on TRAI DLT
NotifyTech AI’s transactional SMS runs on TRAI DLT with approved templates and priority routing, from ₹0.25/msg, with delivery events sent to your webhook.