top of page

How to Send SMS from Your Application — Developer Integration Guide (2026)


Person using a laptop and holding a smartphone, with text "Send SMS from Application" and "TECHTO NETWORKS" logo visible. Vibrant colors.


1. Why Every App Needs to Send SMS in 2026 {#why-every-app}

India crossed 186.2 billion SMS messages in 2025, and that number is climbing. With over 1.1 billion mobile subscribers and smartphone penetration deepening across Tier 2 and Tier 3 cities, SMS is no longer a legacy fallback — it is the most reliable communication layer any application can rely on.

The ability to send SMS from an application powers several mission-critical flows:

  • Two-factor authentication (2FA) and OTP delivery remain the most fraud-resistant user verification method.

  • Transaction alerts reach users even when push notifications are disabled or the app is uninstalled.

  • Logistics and delivery updates hit 99%+ open rates — impossible for email or in-app notification to replicate.

  • Appointment reminders in healthcare reduce no-shows by up to 40%, according to sector studies.

  • Government and fintech notifications require TRAI-compliant delivery audit trails.

Choosing a reliable SMS API and integrating it correctly is the difference between a product that users trust and one that leaks critical communication. This guide gives you everything: real code, real error handling, real DLT headers, and real delivery optimisation — for every major programming language and platform.

2. How SMS Integration Works — Architecture Overview {#architecture}

Before writing any code, understand the flow of a single SMS sent from your application:

Your App Code
    │
    ▼
HTTP POST Request  ──────────────────────────────────────────────────────────────────────
    │                 Auth: API Key + Sender ID + DLT Template ID + Mobile + Message
    ▼
TechTo SMS Gateway  (DLT check → Template match → Spam filter → Operator selection)
    │
    ▼
Telecom Operator (Airtel / Jio / BSNL / Vi)
    │
    ▼
End User's Handset
    │
    ▼
Delivery Receipt (DLR)  ────────►  Webhook callback to your server (optional)

Every step matters. Most integration failures in India happen at the DLT check layer (wrong template ID or mismatched content) or the operator layer (DND filtering on promotional routes). TechTo's gateway handles operator selection dynamically, but your code must supply the correct DLT parameters.

Core concepts you need to know:

Sender ID (Header): A 6-character alphabetic code (e.g., TECHTO) that identifies your brand. Must be registered on a TRAI DLT platform. Transactional headers bypass DND; promotional headers do not.

DLT Template ID: A 19-digit number assigned to each approved message template on the DLT platform. Every API call must include this if you want guaranteed delivery.

Message Route: transactional, promotional, or otp. Wrong route = delivery failure. OTP uses the fastest, highest-priority route.

Delivery Receipt (DLR): An asynchronous callback from the gateway to your server, confirming final handset delivery status. Without DLR handling, you're flying blind.

3. Types of SMS You Can Send from an Application {#types}

SMS Type

Route

DND Bypass

Use Case

Max Length

Transactional

transactional

✅ Yes

OTP, alerts, banking, order status

160 chars / 153 chars (concatenated)

Promotional

promotional

❌ No

Offers, campaigns, broadcasts

160 chars

OTP

otp

✅ Yes

Login, payment, registration

160 chars

Service Implicit

service

✅ Yes

Service notifications to opted-in users

160 chars

Developer Note: In India, sending a promotional message on a transactional route is a TRAI violation and can lead to your Sender ID being blacklisted. Always match the route to the message type.

4. Understanding India's DLT Compliance Before You Write a Single Line of Code {#dlt}

TRAI (Telecom Regulatory Authority of India) mandates that every commercial SMS sent in India goes through the Distributed Ledger Technology (DLT) system. This is not optional — non-compliant messages are blocked at the operator level.

DLT Registration Checklist for Developers

Before your first API call in production, your team must complete these steps on a TRAI-approved DLT platform (Airtel, Jio, BSNL, Vodafone Idea, Tanla, Videocon, etc.):

Step 1 — Entity Registration Register your company (Principal Entity / PE) on any one DLT platform. You'll receive a PE ID (19-digit number).

Step 2 — Header (Sender ID) Registration Register your 6-character Sender ID (e.g., TECHTO, BKONLY, ODRSTS). Every header must be linked to your PE ID. TechTo Networks can register headers on your behalf as a telemarketer.

Step 3 — Template Registration Register every message template with variable placeholders. Example:

Your OTP for {#var#} login is {#var#}. Valid for {#var#} minutes. Do not share. - TECHTO

Each approved template gets a Template ID (19-digit number). You must pass this in every API call.

Step 4 — Telemarketer Registration (Optional) If you're using a third-party provider like TechTo Networks as your SMS gateway, they act as your Telemarketer. Their TM ID is pre-registered with operators.

DLT Variable Rules

  • Placeholders must be written as {#var#} during registration.

  • At send time, replace each {#var#} with the actual value.

  • The structure of the final message must match the registered template exactly. Adding punctuation, spaces, or extra words outside the variables will cause delivery failure.

Example Template:

Template (registered): Your OTP is {#var#}. Valid for {#var#} minutes. - TECHTO
Correct final message: Your OTP is 492817. Valid for 10 minutes. - TECHTO
Wrong final message:   OTP: 492817. Valid for 10 mins. Use immediately. - TECHTO  ← BLOCKED

5. TechTo Networks SMS API — Endpoints, Auth & Request Structure {#api}

Base URL

Authentication

TechTo uses API Key + Secret or HTTP Basic Auth depending on the endpoint. The recommended method is API Key in the request header:

Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

Never expose your API key in client-side JavaScript, mobile app binaries, or public repositories. Use environment variables or a server-side proxy.

Core Send SMS Endpoint

POST https://api.techtonetworks.com/api/v2/bulksms/

Required Parameters

Parameter

Type

Description

APIKey

string

Your TechTo API key

senderid

string

Registered 6-char Sender ID

channel

string

Trans, Promo, or OTP

DCS

string

0 for plain text, 8 for Unicode

flashsms

string

0 (default), 1 for flash SMS

number

string

Mobile number(s) with country code, comma-separated

text

string

Message content (must match DLT template)

route

string

1 = Transactional, 4 = Promotional

DLTTemplateId

string

19-digit DLT Template ID

PEID

string

Your 19-digit DLT Principal Entity ID

Sample JSON Payload

{
  "APIKey": "your_api_key_here",
  "senderid": "TECHTO",
  "channel": "Trans",
  "DCS": "0",
  "flashsms": "0",
  "number": "919876543210",
  "text": "Your OTP is 492817. Valid for 10 minutes. - TECHTO",
  "route": "1",
  "DLTTemplateId": "1007163983456378",
  "PEID": "1201163574512345"
}

Sample Success Response

{
  "ErrorCode": "000",
  "ErrorMessage": "Success",
  "JobId": "TT-20260521-00482910",
  "MessageData": [
    {
      "Number": "919876543210",
      "MessageId": "TT-MSG-92837461",
      "Status": "Queued"
    }
  ]
}

Common Error Codes

Code

Meaning

Action

000

Success

None

002

Invalid API key

Check auth credentials

004

Insufficient credits

Top up account

010

Invalid Sender ID

Verify DLT header registration

020

Template mismatch

Check DLT Template ID and message body

028

DND number

Message filtered (promotional route only)

031

Invalid mobile number

Validate number format

6. How to Send SMS from a Web Application — Node.js {#nodejs}

Installation

npm install axios dotenv

Environment Variables (.env)

TECHTO_API_KEY=your_api_key_here
TECHTO_SENDER_ID=TECHTO
TECHTO_PE_ID=1201163574512345
TECHTO_TEMPLATE_ID=1007163983456378

Basic Send — Node.js (CommonJS)

// sms.js
require('dotenv').config();
const axios = require('axios');

const TECHTO_API_URL = 'https://api.techtonetworks.com/api/v2/bulksms/';

/**
 * Send a single transactional SMS via TechTo Networks API
 * @param {string} mobile - Recipient mobile number with country code (e.g., "919876543210")
 * @param {string} message - Message text matching DLT-registered template
 * @returns {Promise<object>} - API response object
 */
async function sendSMS(mobile, message) {
  const payload = {
    APIKey: process.env.TECHTO_API_KEY,
    senderid: process.env.TECHTO_SENDER_ID,
    channel: 'Trans',
    DCS: '0',
    flashsms: '0',
    number: mobile,
    text: message,
    route: '1',
    DLTTemplateId: process.env.TECHTO_TEMPLATE_ID,
    PEID: process.env.TECHTO_PE_ID,
  };

  try {
    const response = await axios.post(TECHTO_API_URL, payload, {
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${process.env.TECHTO_API_KEY}`,
      },
      timeout: 10000, // 10 second timeout
    });

    if (response.data.ErrorCode !== '000') {
      throw new Error(`SMS API Error ${response.data.ErrorCode}: ${response.data.ErrorMessage}`);
    }

    console.log('SMS sent successfully:', response.data.JobId);
    return response.data;

  } catch (error) {
    if (error.response) {
      // Gateway returned an error response
      console.error('Gateway error:', error.response.status, error.response.data);
    } else if (error.request) {
      // Network timeout or no response
      console.error('Network error — no response received. Consider retry.');
    } else {
      console.error('Request setup error:', error.message);
    }
    throw error;
  }
}

// Usage example
sendSMS('919876543210', 'Your OTP is 492817. Valid for 10 minutes. - TECHTO')
  .then(result => console.log('Job ID:', result.JobId))
  .catch(err => console.error('Failed:', err.message));

module.exports = { sendSMS };

Sending OTP with Retry Logic — Node.js

// otp-sender.js
const { sendSMS } = require('./sms');

const MAX_RETRIES = 3;
const RETRY_DELAY_MS = 2000; // 2 seconds between retries

async function sendOTPWithRetry(mobile, otp, attempt = 1) {
  const message = `Your OTP is ${otp}. Valid for 10 minutes. Do not share. - TECHTO`;

  try {
    const result = await sendSMS(mobile, message);
    return { success: true, messageId: result.MessageData[0].MessageId };
  } catch (err) {
    if (attempt < MAX_RETRIES) {
      console.warn(`Attempt ${attempt} failed. Retrying in ${RETRY_DELAY_MS}ms...`);
      await new Promise(resolve => setTimeout(resolve, RETRY_DELAY_MS * attempt)); // exponential backoff
      return sendOTPWithRetry(mobile, otp, attempt + 1);
    }
    console.error(`All ${MAX_RETRIES} attempts failed for ${mobile}`);
    throw err;
  }
}

module.exports = { sendOTPWithRetry };

Bulk SMS — Node.js (Comma-separated numbers)

async function sendBulkSMS(mobileNumbers, message) {
  const numbersString = mobileNumbers.join(',');
  return sendSMS(numbersString, message);
}

// Usage
sendBulkSMS(
  ['919876543210', '918765432109', '917654321098'],
  'Flash Sale: 40% off all products today. Reply STOP to unsubscribe. - TECHTO'
);

7. How to Send SMS from a Python Application {#python}

Installation

pip install requests python-dotenv

Environment Variables (.env)

TECHTO_API_KEY=your_api_key_here
TECHTO_SENDER_ID=TECHTO
TECHTO_PE_ID=1201163574512345
TECHTO_TEMPLATE_ID=1007163983456378
TECHTO_API_URL=https://api.techtonetworks.com/api/v2/bulksms/

Python SMS Client (Production-Ready)

# techto_sms.py
import os
import time
import requests
from dotenv import load_dotenv

load_dotenv()

class TechToSMSClient:
    """
    TechTo Networks SMS API client.
    Thread-safe, retryable, DLT-compliant.
    """

    API_URL = os.getenv("TECHTO_API_URL", "https://api.techtonetworks.com/api/v2/bulksms/")
    MAX_RETRIES = 3
    TIMEOUT = 10  # seconds

    def __init__(self):
        self.api_key = os.getenv("TECHTO_API_KEY")
        self.sender_id = os.getenv("TECHTO_SENDER_ID")
        self.pe_id = os.getenv("TECHTO_PE_ID")
        self.template_id = os.getenv("TECHTO_TEMPLATE_ID")

        if not all([self.api_key, self.sender_id, self.pe_id, self.template_id]):
            raise ValueError("Missing required TechTo environment variables.")

    def _build_payload(self, mobile: str, message: str, route: str = "Trans") -> dict:
        return {
            "APIKey": self.api_key,
            "senderid": self.sender_id,
            "channel": route,
            "DCS": "0",
            "flashsms": "0",
            "number": mobile,
            "text": message,
            "route": "1" if route == "Trans" else "4",
            "DLTTemplateId": self.template_id,
            "PEID": self.pe_id,
        }

    def send(self, mobile: str, message: str, route: str = "Trans") -> dict:
        """
        Send an SMS to a single mobile number.
        Args:
            mobile: Mobile number with country code (e.g., "919876543210")
            message: DLT-compliant message text
            route: "Trans" for transactional, "Promo" for promotional, "OTP" for OTP
        Returns:
            dict with ErrorCode, JobId, MessageData
        Raises:
            requests.HTTPError, ValueError
        """
        payload = self._build_payload(mobile, message, route)
        headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {self.api_key}",
        }

        for attempt in range(1, self.MAX_RETRIES + 1):
            try:
                response = requests.post(
                    self.API_URL,
                    json=payload,
                    headers=headers,
                    timeout=self.TIMEOUT,
                )
                response.raise_for_status()
                data = response.json()

                if data.get("ErrorCode") == "000":
                    return data
                
                # Non-retryable errors (bad template, invalid number, etc.)
                non_retryable = {"010", "020", "028", "031"}
                if data.get("ErrorCode") in non_retryable:
                    raise ValueError(f"Non-retryable error {data['ErrorCode']}: {data['ErrorMessage']}")

                # Retryable errors (e.g., gateway overload)
                if attempt < self.MAX_RETRIES:
                    wait = 2 ** attempt  # exponential backoff: 2s, 4s
                    print(f"Attempt {attempt} failed ({data['ErrorCode']}). Retrying in {wait}s...")
                    time.sleep(wait)
                else:
                    raise RuntimeError(f"SMS failed after {self.MAX_RETRIES} attempts: {data['ErrorMessage']}")

            except requests.Timeout:
                if attempt < self.MAX_RETRIES:
                    print(f"Timeout on attempt {attempt}. Retrying...")
                    time.sleep(2 ** attempt)
                else:
                    raise

    def send_otp(self, mobile: str, otp: str) -> dict:
        """Convenience method for OTP delivery."""
        message = f"Your OTP is {otp}. Valid for 10 minutes. Do not share. - TECHTO"
        return self.send(mobile, message, route="OTP")

    def send_bulk(self, mobile_numbers: list, message: str) -> dict:
        """Send SMS to multiple numbers (comma-separated batch)."""
        numbers_str = ",".join(mobile_numbers)
        return self.send(numbers_str, message, route="Promo")


# Usage
if __name__ == "__main__":
    client = TechToSMSClient()

    # OTP
    result = client.send_otp("919876543210", "847291")
    print(f"OTP sent. Job ID: {result['JobId']}")

    # Transactional alert
    result = client.send(
        "919876543210",
        "Your order #ORD-98723 has been shipped. Track at techto.in/track - TECHTO",
        route="Trans"
    )
    print(f"Alert sent. Message ID: {result['MessageData'][0]['MessageId']}")

Django / Flask Integration — Python

# In a Django view or Flask route:
from techto_sms import TechToSMSClient
from django.http import JsonResponse

sms_client = TechToSMSClient()  # Instantiate once, reuse (thread-safe)

def send_otp_view(request):
    mobile = request.POST.get("mobile")
    otp = generate_otp()  # your OTP generation logic
    
    try:
        result = sms_client.send_otp(mobile, otp)
        # Store OTP in Redis/DB with TTL
        cache.set(f"otp:{mobile}", otp, timeout=600)
        return JsonResponse({"status": "sent", "job_id": result["JobId"]})
    except ValueError as e:
        return JsonResponse({"status": "error", "message": str(e)}, status=400)
    except Exception as e:
        return JsonResponse({"status": "error", "message": "SMS delivery failed"}, status=500)

8. How to Send SMS from a PHP Application {#php}

Using cURL (No library dependency)

<?php
// techto-sms.php

class TechToSMS {
    private string $apiKey;
    private string $senderId;
    private string $peId;
    private string $templateId;
    private string $apiUrl = 'https://api.techtonetworks.com/api/v2/bulksms/';
    private int $maxRetries = 3;

    public function __construct() {
        $this->apiKey     = getenv('TECHTO_API_KEY');
        $this->senderId   = getenv('TECHTO_SENDER_ID');
        $this->peId       = getenv('TECHTO_PE_ID');
        $this->templateId = getenv('TECHTO_TEMPLATE_ID');
    }

    /**
     * Send SMS via TechTo Networks API
     * @param string $mobile Mobile number with country code (e.g., "919876543210")
     * @param string $message DLT-compliant message text
     * @param string $route "Trans" | "Promo" | "OTP"
     * @return array API response
     * @throws RuntimeException on failure
     */
    public function send(string $mobile, string $message, string $route = 'Trans'): array {
        $payload = json_encode([
            'APIKey'        => $this->apiKey,
            'senderid'      => $this->senderId,
            'channel'       => $route,
            'DCS'           => '0',
            'flashsms'      => '0',
            'number'        => $mobile,
            'text'          => $message,
            'route'         => ($route === 'Promo') ? '4' : '1',
            'DLTTemplateId' => $this->templateId,
            'PEID'          => $this->peId,
        ]);

        for ($attempt = 1; $attempt <= $this->maxRetries; $attempt++) {
            $ch = curl_init($this->apiUrl);
            curl_setopt_array($ch, [
                CURLOPT_POST           => true,
                CURLOPT_POSTFIELDS     => $payload,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_TIMEOUT        => 10,
                CURLOPT_HTTPHEADER     => [
                    'Content-Type: application/json',
                    'Authorization: Bearer ' . $this->apiKey,
                ],
            ]);

            $body  = curl_exec($ch);
            $errno = curl_errno($ch);
            curl_close($ch);

            if ($errno) {
                if ($attempt < $this->maxRetries) {
                    sleep(pow(2, $attempt));
                    continue;
                }
                throw new RuntimeException("cURL error after {$this->maxRetries} attempts: " . curl_strerror($errno));
            }

            $data = json_decode($body, true);

            if (($data['ErrorCode'] ?? '') === '000') {
                return $data;
            }

            // Non-retryable errors
            $nonRetryable = ['010', '020', '028', '031'];
            if (in_array($data['ErrorCode'] ?? '', $nonRetryable)) {
                throw new RuntimeException("SMS error {$data['ErrorCode']}: {$data['ErrorMessage']}");
            }

            if ($attempt < $this->maxRetries) {
                sleep(pow(2, $attempt));
            }
        }

        throw new RuntimeException("SMS failed after {$this->maxRetries} attempts.");
    }

    public function sendOTP(string $mobile, string $otp): array {
        $message = "Your OTP is {$otp}. Valid for 10 minutes. Do not share. - TECHTO";
        return $this->send($mobile, $message, 'OTP');
    }
}

// Usage
$sms = new TechToSMS();

try {
    $result = $sms->sendOTP('919876543210', '738291');
    echo "OTP sent. Job ID: " . $result['JobId'] . PHP_EOL;
} catch (RuntimeException $e) {
    error_log("SMS failed: " . $e->getMessage());
}
?>

Laravel Integration (PHP)

// app/Services/SmsService.php
namespace App\Services;

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

class SmsService {
    public function sendOtp(string $mobile, string $otp): bool {
        $response = Http::timeout(10)
            ->withToken(config('services.techto.api_key'))
            ->post(config('services.techto.api_url'), [
                'APIKey'        => config('services.techto.api_key'),
                'senderid'      => config('services.techto.sender_id'),
                'channel'       => 'OTP',
                'DCS'           => '0',
                'flashsms'      => '0',
                'number'        => $mobile,
                'text'          => "Your OTP is {$otp}. Valid for 10 minutes. Do not share. - TECHTO",
                'route'         => '1',
                'DLTTemplateId' => config('services.techto.template_id'),
                'PEID'          => config('services.techto.pe_id'),
            ]);

        if ($response->successful() && $response->json('ErrorCode') === '000') {
            Log::info('OTP SMS sent', ['mobile' => $mobile, 'job_id' => $response->json('JobId')]);
            return true;
        }

        Log::error('OTP SMS failed', ['error' => $response->json('ErrorMessage')]);
        return false;
    }
}

9. How to Send SMS from a Java Application {#java}

Maven Dependency

<!-- pom.xml -->
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.12.0</version>
</dependency>
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.10.1</version>
</dependency>

Java SMS Client

// TechToSmsClient.java
import com.google.gson.Gson;
import okhttp3.*;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class TechToSmsClient {

    private static final String API_URL = "https://api.techtonetworks.com/api/v2/bulksms/";
    private static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
    private static final int MAX_RETRIES = 3;

    private final OkHttpClient httpClient;
    private final Gson gson;
    private final String apiKey;
    private final String senderId;
    private final String peId;
    private final String templateId;

    public TechToSmsClient(String apiKey, String senderId, String peId, String templateId) {
        this.apiKey     = apiKey;
        this.senderId   = senderId;
        this.peId       = peId;
        this.templateId = templateId;
        this.httpClient = new OkHttpClient.Builder()
            .connectTimeout(10, java.util.concurrent.TimeUnit.SECONDS)
            .readTimeout(10, java.util.concurrent.TimeUnit.SECONDS)
            .build();
        this.gson = new Gson();
    }

    /**
     * Send an SMS via TechTo Networks API.
     * @param mobile  Mobile number with country code (e.g., "919876543210")
     * @param message DLT-compliant message text
     * @param route   "Trans", "Promo", or "OTP"
     * @return API response as Map
     */
    public Map<String, Object> sendSms(String mobile, String message, String route) throws IOException {
        Map<String, Object> payload = new HashMap<>();
        payload.put("APIKey", apiKey);
        payload.put("senderid", senderId);
        payload.put("channel", route);
        payload.put("DCS", "0");
        payload.put("flashsms", "0");
        payload.put("number", mobile);
        payload.put("text", message);
        payload.put("route", "Promo".equals(route) ? "4" : "1");
        payload.put("DLTTemplateId", templateId);
        payload.put("PEID", peId);

        String json = gson.toJson(payload);

        for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
            RequestBody body = RequestBody.create(json, JSON);
            Request request = new Request.Builder()
                .url(API_URL)
                .addHeader("Authorization", "Bearer " + apiKey)
                .post(body)
                .build();

            try (Response response = httpClient.newCall(request).execute()) {
                if (!response.isSuccessful()) {
                    if (attempt < MAX_RETRIES) {
                        Thread.sleep((long) Math.pow(2, attempt) * 1000);
                        continue;
                    }
                    throw new IOException("Unexpected HTTP response: " + response.code());
                }

                String responseBody = response.body().string();
                Map<String, Object> result = gson.fromJson(responseBody, Map.class);

                if ("000".equals(result.get("ErrorCode"))) {
                    return result;
                }

                String errorCode = (String) result.get("ErrorCode");
                if ("010".equals(errorCode) || "020".equals(errorCode) || "031".equals(errorCode)) {
                    throw new RuntimeException("Non-retryable SMS error: " + result.get("ErrorMessage"));
                }

                if (attempt < MAX_RETRIES) {
                    Thread.sleep((long) Math.pow(2, attempt) * 1000);
                }

            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new IOException("SMS send interrupted", e);
            }
        }

        throw new IOException("SMS failed after " + MAX_RETRIES + " attempts.");
    }

    public Map<String, Object> sendOtp(String mobile, String otp) throws IOException {
        String message = "Your OTP is " + otp + ". Valid for 10 minutes. Do not share. - TECHTO";
        return sendSms(mobile, message, "OTP");
    }
}

10. How to Send SMS from an Android App (Kotlin) {#android}

Important: Never call the SMS gateway API directly from an Android or iOS app. Your API key would be exposed in the APK/IPA and can be extracted. Always route through your backend server.
// In your backend-calling ViewModel or Repository
// smsRepository.kt

import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.Body
import retrofit2.http.Header
import retrofit2.http.POST

data class SmsRequest(
    val APIKey: String,
    val senderid: String,
    val channel: String,
    val DCS: String = "0",
    val flashsms: String = "0",
    val number: String,
    val text: String,
    val route: String,
    val DLTTemplateId: String,
    val PEID: String
)

data class SmsResponse(
    val ErrorCode: String,
    val ErrorMessage: String,
    val JobId: String?
)

interface TechToApiService {
    @POST("bulksms/")
    suspend fun sendSms(
        @Header("Authorization") token: String,
        @Body request: SmsRequest
    ): SmsResponse
}

// Call via your own backend server endpoint instead of API directly:
// POST https://yourbackend.com/api/send-otp  { mobile: "91..." }
// Your server then calls TechTo API securely.

Android — Triggering OTP Flow (Kotlin Coroutines)

// In ViewModel
viewModelScope.launch {
    try {
        val response = yourBackendApi.requestOtp(mobile = "919876543210")
        _uiState.value = UiState.OtpSent(message = "OTP sent to your number")
    } catch (e: HttpException) {
        _uiState.value = UiState.Error("Failed to send OTP: ${e.message}")
    }
}

11. How to Send SMS from an iOS App (Swift) {#ios}

Same principle applies — never call the SMS API from your iOS app directly. Use a backend relay.

// SMSService.swift — calls YOUR backend, not TechTo directly

import Foundation

struct OTPRequest: Codable {
    let mobile: String
}

struct OTPResponse: Codable {
    let status: String
    let jobId: String?
    let error: String?
}

class SMSService {
    private let backendURL = URL(string: "https://yourbackend.com/api/send-otp")!

    func requestOTP(mobile: String, completion: @escaping (Result<OTPResponse, Error>) -> Void) {
        var request = URLRequest(url: backendURL)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")

        let body = OTPRequest(mobile: mobile)
        request.httpBody = try? JSONEncoder().encode(body)

        URLSession.shared.dataTask(with: request) { data, response, error in
            if let error = error {
                completion(.failure(error))
                return
            }
            guard let data = data else {
                completion(.failure(NSError(domain: "SMSService", code: -1, userInfo: nil)))
                return
            }
            do {
                let result = try JSONDecoder().decode(OTPResponse.self, from: data)
                completion(.success(result))
            } catch {
                completion(.failure(error))
            }
        }.resume()
    }
}

12. Webhook Setup — Real-Time Delivery Reports {#webhooks}

A Delivery Receipt (DLR) webhook is an HTTP callback that TechTo's gateway sends to your server the moment a message's delivery status changes. Without it, you have no way to know if the SMS was actually delivered.

Step 1 — Configure Your Webhook URL in TechTo Dashboard

Log in → Settings → Webhooks → Enter your callback URL:

Step 2 — Implement the Webhook Receiver

Node.js (Express)

// webhooks/sms-delivery.js
const express = require('express');
const router = express.Router();

// POST /webhooks/sms-delivery
router.post('/sms-delivery', express.json(), (req, res) => {
    const {
        MessageId,
        Status,       // "DELIVERED", "FAILED", "PENDING", "DND", "INVALID"
        Number,
        DeliveryTime,
        ErrorCode,
    } = req.body;

    console.log(`DLR received: MessageId=${MessageId}, Status=${Status}, Number=${Number}`);

    // Update your DB
    updateMessageStatus(MessageId, Status, DeliveryTime).catch(console.error);

    // Always respond 200 quickly — TechTo retries if it gets non-200
    res.status(200).json({ received: true });
});

async function updateMessageStatus(messageId, status, deliveryTime) {
    // Update your messages table
    // await db.query('UPDATE sms_log SET status = $1, delivered_at = $2 WHERE message_id = $3', [status, deliveryTime, messageId]);
}

module.exports = router;

Python (Flask)

# webhooks.py
from flask import Flask, request, jsonify
import logging

app = Flask(__name__)

@app.route('/webhooks/sms-delivery', methods=['POST'])
def sms_delivery_webhook():
    data = request.get_json()
    
    message_id   = data.get('MessageId')
    status       = data.get('Status')   # "DELIVERED", "FAILED", "DND", "PENDING"
    number       = data.get('Number')
    error_code   = data.get('ErrorCode')
    
    logging.info(f"DLR: {message_id} → {status} for {number}")
    
    if status == 'DELIVERED':
        # Mark message as delivered in DB
        pass
    elif status in ('FAILED', 'DND', 'INVALID'):
        # Trigger fallback (e.g., voice call or email)
        pass
    
    return jsonify({'received': True}), 200

DLR Status Values

Status

Meaning

Action

DELIVERED

Message reached handset

Log success

PENDING

In transit, no final report yet

Poll again later

FAILED

Gateway could not deliver

Retry or fallback

DND

Number on DND registry

Remove from lists

INVALID

Number doesn't exist

Remove from lists

EXPIRED

Delivery window passed

Flag for review

13. Error Handling and Retry Logic {#errors}

Professional SMS integration always handles errors at three distinct layers:

Layer 1 — Network / HTTP Errors

// These mean TechTo's server wasn't reached at all
// - ECONNREFUSED: Server down
// - ETIMEDOUT: No response within timeout
// - ENOTFOUND: DNS failure

// Always retry with exponential backoff:
const delay = Math.pow(2, attempt) * 1000; // 2s, 4s, 8s...

Layer 2 — Gateway-Level Errors (HTTP 200, ErrorCode !== "000")

RETRYABLE_CODES = set()  # Most gateway errors are non-retryable
NON_RETRYABLE_CODES = {
    "002": "Invalid API key — check credentials",
    "004": "Insufficient credits — top up account",
    "010": "Invalid Sender ID — check DLT header",
    "020": "Template mismatch — check DLT template ID and message body",
    "028": "DND number — remove from promotional lists",
    "031": "Invalid number — validate before sending",
}

# If code is in NON_RETRYABLE_CODES: raise immediately, do not retry
# Log the error with full context for debugging

Layer 3 — Business Logic (Delivered but wrong)

// When DLR webhook fires with status=FAILED:
// 1. Check ErrorCode for root cause
// 2. If route mismatch or template issue — fix and resend
// 3. If DND — flag the number, never retry on promotional route
// 4. If EXPIRED — check delivery window settings in TechTo dashboard
// 5. Consider fallback channels (WhatsApp, email, voice)

14. OTP SMS Integration — Special Considerations {#otp}

OTP delivery is the most latency-sensitive use case. A 5-second delay on a login OTP degrades UX significantly; a 30-second delay causes users to abandon sign-up.

OTP-Specific Best Practices

Use the OTP route, not Trans route. The OTP route uses the highest-priority operator channels and bypasses all DND filters.

Keep OTP messages under 160 characters. Concatenated SMS (messages over 160 chars) takes 2× longer and costs 2× more. A well-designed OTP template is under 100 characters.

✅ Good: "Your OTP is 492817. Valid 10 mins. Do not share. - TECHTO"  (58 chars)
❌ Bad:  "Dear Valued Customer, your One Time Password for verifying your account on our platform is 492817. This is valid for the next ten (10) minutes."  (144 chars — close to limit, risk of split)

Generate cryptographically secure OTPs. Never use Math.random().

// Node.js — secure OTP generation
const crypto = require('crypto');
function generateOTP(length = 6) {
    return crypto.randomInt(Math.pow(10, length - 1), Math.pow(10, length)).toString();
}
# Python — secure OTP generation
import secrets
def generate_otp(length: int = 6) -> str:
    return str(secrets.randbelow(10**length)).zfill(length)

OTP expiry and rate limiting. Always expire OTPs server-side. Never rely solely on the message text saying "valid for 10 minutes" — enforce this in your backend:

# Redis-based OTP storage with TTL
import redis
r = redis.Redis()

def store_otp(mobile: str, otp: str, ttl_seconds: int = 600):
    r.setex(f"otp:{mobile}", ttl_seconds, otp)

def verify_otp(mobile: str, user_input: str) -> bool:
    stored = r.get(f"otp:{mobile}")
    if not stored:
        return False  # Expired or never sent
    if stored.decode() == user_input:
        r.delete(f"otp:{mobile}")  # Invalidate after use
        return True
    return False

Rate limit OTP sends. Prevent OTP bombing attacks:

def can_send_otp(mobile: str, max_attempts: int = 5, window_seconds: int = 3600) -> bool:
    key = f"otp_attempts:{mobile}"
    attempts = r.incr(key)
    if attempts == 1:
        r.expire(key, window_seconds)
    return attempts <= max_attempts

15. Security Best Practices {#security}

Never Expose Your API Key

// ❌ WRONG — API key in client-side React code
const SMS_KEY = "tt_live_abc123"; // Exposed in browser source

// ✅ CORRECT — all SMS calls go through your backend
// Frontend → POST /api/send-otp → Backend → TechTo API

Secure Your Webhook Endpoint

TechTo signs webhook payloads with an HMAC-SHA256 signature. Always verify it:

// Node.js webhook signature verification
const crypto = require('crypto');

function verifyWebhookSignature(rawBody, signature, secret) {
    const expected = crypto
        .createHmac('sha256', secret)
        .update(rawBody)
        .digest('hex');
    return crypto.timingSafeEqual(
        Buffer.from(expected, 'hex'),
        Buffer.from(signature, 'hex')
    );
}

app.post('/webhooks/sms-delivery', express.raw({ type: 'application/json' }), (req, res) => {
    const sig = req.headers['x-techto-signature'];
    if (!verifyWebhookSignature(req.body, sig, process.env.WEBHOOK_SECRET)) {
        return res.status(401).json({ error: 'Invalid signature' });
    }
    // Process the verified DLR
    res.status(200).json({ received: true });
});

Use TLS 1.2+ for All API Calls

All TechTo API endpoints require HTTPS. Never use HTTP in production. Your HTTP client should enforce this:

# Python — enforce TLS verification
import ssl
import certifi

# requests uses certifi by default — do NOT set verify=False
response = requests.post(url, json=payload, verify=True)  # ✅ correct
response = requests.post(url, json=payload, verify=False)  # ❌ never do this

Input Validation Before Every API Call

function validateIndianMobile(number) {
    // Indian mobile: 10 digits, starting with 6-9
    const cleaned = number.replace(/\D/g, '');
    const withCountryCode = cleaned.startsWith('91') ? cleaned : `91${cleaned}`;
    return /^91[6-9]\d{9}$/.test(withCountryCode) ? withCountryCode : null;
}

16. Testing Your Integration in Sandbox Mode {#sandbox}

TechTo provides a sandbox environment that simulates real API responses without consuming credits or sending actual messages.

Sandbox Configuration

# .env.test
TECHTO_API_URL=https://sandbox.api.techtonetworks.com/api/v2/bulksms/
TECHTO_API_KEY=sandbox_test_key_here

Sandbox Test Numbers

Use the following mobile numbers to test specific responses:

Test Number

Simulated Response

919000000001

Immediate DELIVERED

919000000002

FAILED (network error)

919000000003

DND blocked

919000000004

PENDING → DELIVERED after 5 seconds

919000000005

INVALID number

Running Integration Tests (Node.js/Jest)

// sms.test.js
const { sendSMS } = require('./sms');
process.env.TECHTO_API_URL = 'https://sandbox.api.techtonetworks.com/api/v2/bulksms/';

describe('TechTo SMS API', () => {
    test('sends OTP successfully to sandbox number', async () => {
        const result = await sendSMS('919000000001', 'Your OTP is 123456. Valid for 10 minutes. - TECHTO');
        expect(result.ErrorCode).toBe('000');
        expect(result.JobId).toBeDefined();
    });

    test('handles DND number gracefully', async () => {
        await expect(sendSMS('919000000003', 'Test promo message - TECHTO'))
            .rejects.toThrow('028');
    });
});

17. Troubleshooting Common SMS API Errors in India {#troubleshooting}

"Template mismatch" (Error 020)

Cause: The message text sent via API doesn't match the DLT-registered template structure.

Fix: Log the exact text parameter being sent. Compare character-by-character with the DLT-registered template. Even extra spaces or different punctuation causes this error.

# Debug utility — log before sending
def debug_sms_payload(text: str, template_id: str):
    print(f"Message ({len(text)} chars): '{text}'")
    print(f"Template ID: {template_id}")
    print(f"Variable placeholders filled: {text.count('{') == 0}")

Messages Delivered to Some Operators, Not Others

Cause: If your header is registered on only one DLT platform (e.g., Airtel DLT), it may not federate to all operators instantly.

Fix: Register your Sender ID on TRAI's common DLT platform or ensure your provider (TechTo) handles cross-operator template syndication.

OTP Delivery Delayed During Peak Hours

Cause: Operator-level congestion on shared routes during peak hours (9am–11am, 6pm–8pm IST).

Fix: Use TechTo's OTP route (highest priority queue), keep messages under 160 chars, and implement a fallback timer in your UX ("Resend OTP after 30 seconds").

Unicode Messages Showing as Garbled Text

Cause: The DCS parameter is set to 0 (GSM-7 encoding) for a message containing Hindi/Tamil/Bengali characters.

Fix: Set DCS: "8" for Unicode messages. Note: Unicode SMS limit is 70 characters per segment (vs. 160 for GSM-7).

{
  "DCS": "8",
  "text": "आपका OTP 492817 है। 10 मिनट में वैध। साझा न करें। - TECHTO"
}

18. Industry Use Cases — When to Send SMS from an Application {#usecases}

E-commerce

  • Order confirmation immediately after payment

  • Shipping dispatch and delivery notifications

  • Cart abandonment reminders (45% cart recovery uplift, per internal data)

  • Return and refund status updates

Fintech & Banking

  • Transaction alerts (debit/credit) — DND-bypass via transactional route

  • OTP for payment authorization, login, and beneficiary addition

  • EMI reminders 3 days before due date

  • KYC verification and document submission prompts

Healthcare

  • Appointment reminders 24 hours and 2 hours before

  • Lab report ready notifications

  • Prescription pickup reminders

  • Telemedicine session links via SMS (for feature phone users)

Logistics

  • Pickup confirmation for courier bookings

  • Real-time shipment tracking updates

  • Delivery attempt notifications

  • Customer satisfaction surveys post-delivery

EdTech

  • Class schedule reminders

  • Fee payment reminders

  • Result and grade notifications

  • Exam hall ticket availability

Government Services

  • Scheme beneficiary notifications

  • Document submission acknowledgements

  • Grievance redressal status updates

  • Election/voter awareness campaigns (using registered headers)

19. TechTo SMS API vs Competitors — Feature Comparison {#comparison}

Feature

TechTo Networks

MSG91

Fast2SMS

TextLocal

DLT-Compliant by Default

OTP Route (Highest Priority)

HTTP Code Samples (Python/PHP/Node/Java)

Partial

Partial

Sandbox Environment

Webhook DLR Delivery Reports

Unicode SMS Support

Reseller/Sub-Account API

RCS Messaging (2026)

WhatsApp Business API

99.99% Uptime SLA

Dedicated Developer Support

20. Frequently Asked Questions {#faq}

Q1. Can I send SMS from a web application without a backend server? No. You must never expose your API key in front-end JavaScript. All SMS API calls must go through your backend server (Node.js, Python, PHP, Java, etc.), which then calls the TechTo API with your secured credentials.

Q2. What is the character limit for an SMS in India? For English (GSM-7 encoding): 160 characters per segment. Messages over 160 characters are split into multiple parts with headers, reducing each segment to 153 usable characters. For Unicode (Hindi, Tamil, etc.) with DCS=8: 70 characters per segment (67 for multipart). You are charged per segment.

Q3. How long does it take for an SMS to be delivered in India? On TechTo's OTP route: average delivery in under 3 seconds for most operators. Transactional route: 3–8 seconds average. Promotional route: 1–30 seconds depending on operator congestion and time of day.

Q4. What is a DLT Template ID and where do I get it? When you register your message template on a TRAI DLT platform (Airtel, Jio, BSNL, etc.), you receive a 19-digit Template ID upon approval. You must pass this in every API call as DLTTemplateId. TechTo Networks can assist you with DLT registration.

Q5. How do I handle delivery failures in production? Implement three layers: (1) Retry on network errors with exponential backoff. (2) On DLR FAILED or EXPIRED status, check the error code — fix template/route issues before retrying. (3) For critical flows (OTP, banking alerts), implement a fallback channel (WhatsApp, voice call). Never spam retry — set a maximum of 3 attempts.

Q6. Can I send SMS in Hindi, Tamil, or other Indian languages? Yes. Set DCS: "8" in your API request to enable Unicode mode. The message limit drops to 70 characters per segment. TechTo supports all 22 scheduled Indian languages.

Q7. What is the difference between a transactional and promotional SMS route? Transactional SMS: sent on a DND-bypassing route for service messages (OTPs, alerts, order updates). Cannot be used for offers or unsolicited promotions. Promotional SMS: used for marketing campaigns; blocked for DND-registered numbers (8 AM to 9 PM sending window mandated by TRAI). Using the wrong route causes delivery failure or TRAI violations.

Q8. Does TechTo Networks offer an SMS API free trial? Yes. Register at techtonetworks.com to access sandbox credentials and test credits. No credit card required for sandbox testing.

Q9. How do I register a DLT Sender ID? Register your entity on any TRAI-approved DLT portal. Then submit your 6-character Sender ID for approval. Approval typically takes 24–72 hours. TechTo Networks can facilitate registration as your Telemarketer. Contact the support team with your PE ID and desired header.

Q10. Is it possible to send SMS internationally from my application using TechTo? Yes. TechTo supports international SMS delivery to 150+ countries via Tier-1 routes. API parameters remain the same; remove DLT-specific fields (not required outside India) and use the appropriate country code in the number field.

Ready to Integrate? Start Sending SMS from Your Application Today

TechTo Networks gives you everything you need to integrate SMS into any application — web, mobile, or backend — with full DLT compliance, multi-language support, and the fastest OTP delivery routes in India.

Get started in under 10 minutes:

  1. Register for a free account — get your API key instantly.

  2. Download the SDK or use the code samples in this guide.

  3. Test in sandbox mode at zero cost.

  4. Go live — your first 100 SMS are on us.

For DLT registration support, custom integration help, or enterprise pricing, contact the TechTo developer team.

About This Guide Written by the TechTo Networks developer team. Last updated: May 2026. All code samples tested against TechTo API v2. Compliant with TRAI DLT regulations, India's Digital Personal Data Protection (DPDP) Act 2023, and GDPR where applicable. Feedback or corrections? Open an issue on our GitHub or reach us on WhatsApp.

Related Guides:

Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating
bottom of page