TRAI's November 2025 direction ended the catch-all {#var#}. This guide covers the tag names each operator uses, the length each tag allows, how to migrate old templates, and how to fix "variable exceeded max length" before a message is lost.
Since TRAI's direction of 18 November 2025, every variable in a DLT SMS template has to be tagged with the kind of value it carries. Airtel's portal uses {#numeric#}, {#alphanumeric#}, {#url#}, {#urlott#}, {#cbn#} and {#email#}. Vi and BSNL use {#num#}, {#alp#}, {#urg#}, {#uro#}, {#cbn#} and {#eml#}. Number, text and email values are checked to 40 characters, links to 120 and callback numbers to 3–14 digits. Untagged templates must be migrated.
TRAI's direction F.No. D-2711/(2)/2024-QoS of 18 November 2025 (announced in press release 133/2025) requires each variable field in every SMS content template to be "pre-tagged with descriptive labels specifying the content type, purpose, and validation criteria". The reason, in TRAI's words: the absence of tagging "has been routinely exploited for fraudulent and phishing activities, allowing unregistered or malicious URLs, app links, and callback numbers to be inserted into approved templates".
Directions from February and May 2023 had already asked for this; TRAI notes they "have not been implemented till date". COAI proposed the tag set on 5 September 2025 and all access providers agreed to it on 17 September 2025.
| Step | What the direction says | Works out to |
|---|---|---|
| New templates | Registered only if tagged, from 10 days after the direction | From 28 Nov 2025 |
| Tag scrubbing | Tagged values checked against whitelisted URLs, OTT/APK links and callback numbers within 30 days | By 18 Dec 2025 |
| Existing templates | Modified within 60 days of the start of tag scrubbing | Depends on operator start |
| Logger mode | First 60 days: failures logged, messages still delivered. After that, failing messages are "rejected outright and not delivered". | Depends on operator start |
Operators do not agree on when blocking starts. Airtel's portal states "Effective 15 Jan 2026" for mandatory enforcement, followed by a 60-day logger mode and then blocking. That would put blocking around mid-March 2026. Vi's content template manual, which is undated, says: "As of now, nothing will be dropped… logger mode… will be enabled as per a timeline that will be communicated separately." We could not confirm that hard blocking is live on every operator as of 29 October 2026. Plan as if it is.
TRAI's Annexure-I names the tags with bare hashes. The portals wrap them in braces, and Vi and BSNL shorten them:
| Value type | TRAI Annexure-I | Airtel | Vi and BSNL |
|---|---|---|---|
| Number | #number# / #numeric# | {#numeric#} | {#num#} |
| Letters and numbers | #alphanumeric# | {#alphanumeric#} | {#alp#} |
| Web link | #url# | {#url#} | {#urg#} |
| App or OTT link | #urlott# | {#urlott#} | {#uro#} |
| Callback number | #cbn# | {#cbn#} | {#cbn#} |
#email# | {#email#} | {#eml#} |
Airtel's tag list also contains {#purpose#}, which no public Airtel material explains. Its pre-tagging notice asks for three things per variable: the "Variable Type", the "Purpose of the variable" and a "Sample value matching validation rules". For URL, OTT, callback and email tags, the value dropdown is filled from your whitelisted CTAs. If you have none of that type, Airtel shows "Values not found for type… kindly register for more in Manage CTA".
We could not confirm the tag spelling for Jio TrueConnect or SmartPing from their public portal code, so use what their template screens offer. Operator-by-operator portal notes are in our Jio, Airtel, Vi and BSNL guides.
| Tag (Vi/BSNL) | Max length | Validation at scrubbing |
|---|---|---|
{#num#} | 40 | Digits 0–9 only; no letters or special characters |
{#alp#} | 40 | Letters and numbers; must not start with www, http or https; the only tag allowed back to back |
{#urg#} | 120 | Must match a whitelisted static, dynamic or short URL |
{#uro#} | 120 | Must match a whitelisted OTT or APK link |
{#cbn#} | 3–14 | Must match a whitelisted mobile, landline or toll-free number; no special characters; country code goes in the fixed text |
{#eml#} | 40 | Must be a valid email address |
Sources: the Vi and BSNL content template manuals, and TRAI Annexure-I, which sets "not more than 40 characters" for alphanumeric values. Airtel's equivalents follow the same Annexure. Link formats for whitelisting are covered in our template registration guide.
Search for the variable limit and you will find both numbers. The conflict exists inside Airtel's own portal. Its older template page says "You can pass up to 30 characters excluding exempted special characters in {#var#}". Its newer template component says "up to 40 characters (excluding exempted special characters)". Infobip's help article, updated in July 2026, says 30. MSG91 says 40.
Our reading: 30 applies to legacy untagged {#var#} on Airtel, and 40 applies to tagged number, text and email variables everywhere. Airtel does not list the "exempted special characters", so do not count on them. Until every template you use is tagged, keep free-text values to 30 characters.
| Value | Airtel / Vi-BSNL tag | Watch out for |
|---|---|---|
| OTP, count, whole-rupee amount | {#numeric#} / {#num#} | Commas, decimals and spaces fail; keep "Rs." in the fixed text |
| Order ID, name, date, ticket number | {#alphanumeric#} / {#alp#} | 40-character cap; whether punctuation passes is not published |
| Tracking or payment link | {#url#} / {#urg#} | Whitelist the link, or the base up to "?" for dynamic links |
| WhatsApp, Play Store or APK link | {#urlott#} / {#uro#} | Registered as OTT or APK, not as a URL |
| Helpline or branch number | {#cbn#} | Digits only, and on your CTA list |
| Support email | {#email#} / {#eml#} | Airtel offers only whitelisted emails |
Vi and BSNL provide a bulk CSV tool, described in the same words in both manuals:
{#var#} with a tag. Do not change one character of the fixed text. "Even a single extra space… will cause the row to fail." Keep the same number of variables.Before: Dear {#var#}, your order {#var#} of Rs {#var#} is confirmed. -Acme Stores
Vi/BSNL: Dear {#alp#}, your order {#alp#} of Rs {#num#} is confirmed. -Acme Stores
Airtel: Dear {#alphanumeric#}, your order {#alphanumeric#} of Rs {#numeric#} is confirmed. -Acme Stores
Sample: Dear Priya, your order AZ10234 of Rs 1499 is confirmed. -Acme StoresAirtel's public material describes no bulk migration tool; its template screen swaps each placeholder for the tag you pick from a type dropdown. For existing Airtel templates, ask dlt.care@airtel.com or your telemarketer. One trap: an old {#var#} that carried a link now needs a URL tag and a whitelisted CTA.
Airtel's scrubbing code 635 means "Template variable exceeded max length". The 600-series table used for Vi and BSNL has the same number, TEMPLATE_VARIABLE_EXCEEDED_MAX_LENGTH. Aggregators re-label it; Infobip, for example, reports it as error 4131. The usual causes:
Fixes, in order of preference:
Never truncate an OTP, an amount or a link. A cut-off value is worse than a rejected one, so validate these and stop the send instead. Other codes are covered in our DLT error codes guide.
Scrubbing rejects a message only after you have sent it. This function applies the published limits first and accepts both Airtel and Vi/BSNL tag names. It is a pre-check; the operator's scrubber has the final word.
<?php
// dlt-check.php: catch DLT variable errors before the operator does (PHP 8.0+).
// Limits: TRAI Annexure-I (18 Nov 2025) and the Vi/BSNL template manuals.
// Airtel's long tag names and Vi/BSNL's short ones map to the same rule.
const DLT_TAG_TYPES = [
'numeric' => 'num', 'number' => 'num', 'num' => 'num',
'alphanumeric' => 'alp', 'alp' => 'alp',
'url' => 'url', 'urg' => 'url',
'urlott' => 'ott', 'uro' => 'ott',
'cbn' => 'cbn',
'email' => 'eml', 'eml' => 'eml',
'var' => 'var', // legacy, untagged
];
/**
* Returns a list of problems; an empty array means every value passes.
* $ctas holds your whitelisted values: ['url' => [...], 'ott' => [...], 'cbn' => [...]].
* A whitelisted link ending in "?" or "/" is treated as a prefix (dynamic or
* short URL); anything else must match exactly.
*/
function dlt_check(string $template, array $values, array $ctas = []): array
{
preg_match_all('/\{#(\w+)#\}/', $template, $m);
if (count($m[1]) !== count($values)) {
return [sprintf('Template has %d variables but %d values were given', count($m[1]), count($values))];
}
$errors = [];
foreach (array_values($values) as $i => $value) {
$value = (string) $value;
$tag = strtolower($m[1][$i]);
$type = DLT_TAG_TYPES[$tag] ?? null;
$len = mb_strlen($value);
$at = sprintf('Variable %d {#%s#}', $i + 1, $tag);
$error = match ($type) {
'num' => preg_match('/^[0-9]{1,40}$/', $value) ? null : 'digits only, 1-40 characters',
'alp' => match (true) {
$len > 40 => "$len characters, limit 40",
(bool) preg_match('~https?://|www\.~i', $value) => 'contains a URL; use a URL tag',
default => null,
},
'url', 'ott' => match (true) {
$len > 120 => "$len characters, limit 120",
!dlt_whitelisted($value, $ctas[$type] ?? []) => 'link is not whitelisted',
default => null,
},
'cbn' => match (true) {
!preg_match('/^[0-9]{3,14}$/', $value) => '3-14 digits, no + or spaces',
!in_array($value, $ctas['cbn'] ?? [], true) => 'number is not whitelisted',
default => null,
},
'eml' => ($len <= 40 && filter_var($value, FILTER_VALIDATE_EMAIL)) ? null : 'not a valid email of 40 characters or fewer',
'var' => $len > 30 ? "$len characters; keep untagged {#var#} to 30" : null,
default => 'unknown tag',
};
if ($error !== null) {
$errors[] = "$at: $error";
}
}
return $errors;
}
function dlt_whitelisted(string $link, array $whitelist): bool
{
foreach ($whitelist as $allowed) {
$prefix = str_ends_with($allowed, '?') || str_ends_with($allowed, '/');
if ($link === $allowed || ($prefix && str_starts_with($link, $allowed))) {
return true;
}
}
return false;
}
// Try it
$ctas = [
'url' => ['https://acme.example/track?', 'https://shrt.example/ACMEST/'],
'cbn' => ['18001234567'],
];
$tpl = 'Your Acme Stores order {#alphanumeric#} has been shipped. Track it here: {#url#} -Acme Stores';
$tests = [
[$tpl, ['AZ10234', 'https://acme.example/track?id=AZ10234']],
[$tpl, ['AZ10234 for Priya Ramasamy, Door 12, Kalangal Road', 'https://bit.ly/3xYz']],
['Your OTP is {#num#}. -Acme', ['48 29 13']],
['Call {#cbn#} for help. -Acme', ['+91 1800 123 4567']],
];
foreach ($tests as [$template, $values]) {
$errors = dlt_check($template, $values, $ctas);
echo $errors ? implode("\n", $errors) : 'OK', "\n";
}
// OK
// Variable 1 {#alphanumeric#}: 50 characters, limit 40
// Variable 2 {#url#}: link is not whitelisted
// Variable 1 {#num#}: digits only, 1-40 characters
// Variable 1 {#cbn#}: 3-14 digits, no + or spacesRun it where you build the message, then send with the matching dlt_template_id as shown in our PHP SMS guide. Operators do not publish how they count Tamil or other Indic text against variable limits, so leave a margin.
We tag each variable, whitelist the links and numbers it needs and file the migration on your DLT portal. Then we test live sends through our own gateway. AvanceZone works from Sulur, Coimbatore (bulk SMS in Coimbatore).