urmi-ai/GENERIC_TEMPLATE_SMS.md
2026-10-02 11:22:40 +05:30

12 KiB

Ultronsms — generic DLT template SMS

Parameterized integration for sending any DLT-approved SMS via Ultronsms by supplying peid, dlttemplateid, sender ID, and filled template text at runtime.

Use this when you have multiple approved templates (OTP, password reset, alerts, etc.) and want one reusable client instead of hardcoding each flow.


Overview

┌──────────────┐   peid + dlttemplateid   ┌─────────────────┐   SendSMS    ┌─────────────┐
│  Caller      │   senderid + message     │  Generic client │ ───────────► │  Ultronsms  │
│  (your app)  │ ───────────────────────► │  (this doc)     │              │   gateway   │
└──────────────┘                          └─────────────────┘              └──────┬──────┘
                                                    │                              │
                                                    │  GetDelivery (optional)      │
                                                    ◄──────────────────────────────┘

Caller provides

Input Description
mobile Recipient number (+91… or 91…)
sender_id DLT-approved header, e.g. URMIAI, WEBDMG
pe_id Principal Entity ID linked to that sender
dlt_template_id Approved content template ID
message Final SMS text with all {#var#} / {#num#} placeholders replaced
channel Trans (transactional) or Promo (promotional) — default Trans

Client returns

  • Send response with JobId and MessageId
  • Optional delivery report via GetDelivery

Environment configuration

Store credentials in environment variables (never commit secrets).

Variable Example Description
ULTRONSMS_BASE_URL https://ultronsms.com Gateway base URL
ULTRONSMS_USER GENZENITH Account username
ULTRONSMS_PASSWORD (secret) Account password

Route mapping

Channel channel param route param
Transactional Trans 2
Promotional Promo 1

The UI may show internal route labels (e.g. T_22); the API expects numeric routes above.


API endpoints

Action Method URL
Send SMS GET {BASE_URL}/api/mt/SendSMS
Delivery status GET {BASE_URL}/api/mt/GetDelivery

SendSMS — all parameters

Parameter Required Notes
user yes From ULTRONSMS_USER
password yes From ULTRONSMS_PASSWORD
senderid yes DLT-approved header
channel yes Trans or Promo
route yes 2 (Trans) or 1 (Promo)
number yes Country code + number, no +
text yes Filled DLT template body
peid yes PE ID for this sender
dlttemplateid yes Lowercase parameter name
DCS yes 0 = English, 8 = Unicode
flashsms yes 0 = normal, 1 = flash

GetDelivery parameters

Parameter Required
user yes
password yes
jobid yes — from SendSMS JobId

DLT template rules

  1. Exact match — Sent text must match the approved template except for variable substitutions.
  2. Replace all placeholders — Common patterns: {#var#}, {#num#}. Fill every occurrence.
  3. Sender ↔ PE ID — Each sender ID is bound to a PE ID in the DLT portal; they must match.
  4. Template ID — Use the ID from the approved template row, not the PE ID.

Example templates (verified in testing)

URMI OTP (transactional)

Field Value
Sender URMIAI
PE ID 1701178780498045011
Template ID 1777178797989033323
Template Your URMI app verification code is {#num#}. It is valid for {#num#} minutes. Do not share it to anyone - URMI AI
Example message Your URMI app verification code is 482916. It is valid for 10 minutes. Do not share it to anyone - URMI AI

Password reset (transactional)

Field Value
Sender WEBDMG
PE ID 1301159136621106678
Template ID 1307161537613263303
Template Hello Customer, Your new password is '{#var#}'. Thank you. Regards, WebXion
Example message Hello Customer, Your new password is 'Kx9#mP2q'. Thank you. Regards, WebXion

Python implementation (generic)

import os
import time
from dataclasses import dataclass
from enum import Enum
from typing import Any
from urllib.parse import urlencode
from urllib.request import urlopen
import json


class SmsChannel(str, Enum):
    TRANSACTIONAL = "Trans"
    PROMOTIONAL = "Promo"


@dataclass(frozen=True)
class DltTemplate:
    """Register each approved template in your app or database."""
    sender_id: str
    pe_id: str
    dlt_template_id: str
    channel: SmsChannel = SmsChannel.TRANSACTIONAL

    @property
    def route(self) -> str:
        return "2" if self.channel == SmsChannel.TRANSACTIONAL else "1"


@dataclass
class SendResult:
    success: bool
    job_id: str | None
    message_id: str | None
    number: str | None
    raw: dict[str, Any]


class UltronsmsClient:
    def __init__(
        self,
        base_url: str | None = None,
        user: str | None = None,
        password: str | None = None,
    ) -> None:
        self.base_url = (base_url or os.environ["ULTRONSMS_BASE_URL"]).rstrip("/")
        self.user = user or os.environ["ULTRONSMS_USER"]
        self.password = password or os.environ["ULTRONSMS_PASSWORD"]

    @staticmethod
    def normalize_phone(number: str) -> str:
        digits = "".join(c for c in number if c.isdigit())
        if len(digits) == 10:
            return f"91{digits}"
        return digits

    def _get(self, path: str, params: dict[str, str]) -> dict[str, Any]:
        url = f"{self.base_url}{path}?{urlencode(params)}"
        with urlopen(url, timeout=30) as resp:
            return json.loads(resp.read().decode())

    def send_template_sms(
        self,
        mobile: str,
        message: str,
        template: DltTemplate,
        *,
        dcs: str = "0",
        flash_sms: str = "0",
    ) -> SendResult:
        """
        Send SMS using any DLT-approved template.

        Args:
            mobile: Recipient, e.g. +916296346452
            message: Fully substituted DLT template text
            template: sender_id, pe_id, dlt_template_id, channel
        """
        number = self.normalize_phone(mobile)
        params = {
            "user": self.user,
            "password": self.password,
            "senderid": template.sender_id,
            "channel": template.channel.value,
            "route": template.route,
            "DCS": dcs,
            "flashsms": flash_sms,
            "number": number,
            "text": message,
            "peid": template.pe_id,
            "dlttemplateid": template.dlt_template_id,
        }
        raw = self._get("/api/mt/SendSMS", params)
        ok = raw.get("ErrorCode") == "000"
        message_data = (raw.get("MessageData") or [{}])[0]
        return SendResult(
            success=ok,
            job_id=raw.get("JobId"),
            message_id=message_data.get("MessageId"),
            number=message_data.get("Number"),
            raw=raw,
        )

    def get_delivery(self, job_id: str) -> dict[str, Any]:
        return self._get(
            "/api/mt/GetDelivery",
            {"user": self.user, "password": self.password, "jobid": job_id},
        )

    def wait_for_delivery(
        self,
        job_id: str,
        *,
        timeout_seconds: int = 15,
        poll_interval_seconds: float = 3.0,
    ) -> dict[str, Any]:
        deadline = time.time() + timeout_seconds
        last: dict[str, Any] = {}
        while time.time() < deadline:
            last = self.get_delivery(job_id)
            reports = last.get("DeliveryReports") or []
            if reports and reports[0].get("DeliveryStatus") == "Delivered":
                return last
            time.sleep(poll_interval_seconds)
        return last

Template registry example

TEMPLATES = {
    "urmi_otp": DltTemplate(
        sender_id="URMIAI",
        pe_id="1701178780498045011",
        dlt_template_id="1777178797989033323",
        channel=SmsChannel.TRANSACTIONAL,
    ),
    "webxion_password": DltTemplate(
        sender_id="WEBDMG",
        pe_id="1301159136621106678",
        dlt_template_id="1307161537613263303",
        channel=SmsChannel.TRANSACTIONAL,
    ),
}


def build_message(template_key: str, **variables: str) -> str:
    """Fill placeholders manually per template — keep in sync with DLT approval."""
    builders = {
        "urmi_otp": lambda otp, minutes: (
            f"Your URMI app verification code is {otp}. "
            f"It is valid for {minutes} minutes. "
            f"Do not share it to anyone - URMI AI"
        ),
        "webxion_password": lambda password: (
            f"Hello Customer, Your new password is '{password}'. "
            f"Thank you. Regards, WebXion"
        ),
    }
    return builders[template_key](**variables)

Usage

client = UltronsmsClient()

# URMI OTP
result = client.send_template_sms(
    mobile="+916296346452",
    message=build_message("urmi_otp", otp="482916", minutes="10"),
    template=TEMPLATES["urmi_otp"],
)

# Password reset
result = client.send_template_sms(
    mobile="+919149154337",
    message=build_message("webxion_password", password="Kx9#mP2q"),
    template=TEMPLATES["webxion_password"],
)

if result.success:
    delivery = client.wait_for_delivery(result.job_id)
    print(delivery)
else:
    print(result.raw)  # handle ErrorCode / ErrorMessage

cURL template

Replace {...} placeholders at call time.

curl -sS -L -G "https://ultronsms.com/api/mt/SendSMS" \
  --data-urlencode "user=${ULTRONSMS_USER}" \
  --data-urlencode "password=${ULTRONSMS_PASSWORD}" \
  --data-urlencode "senderid={SENDER_ID}" \
  --data-urlencode "channel=Trans" \
  --data-urlencode "DCS=0" \
  --data-urlencode "flashsms=0" \
  --data-urlencode "number={MOBILE_91XXXXXXXXXX}" \
  --data-urlencode "text={FILLED_DLT_MESSAGE}" \
  --data-urlencode "route=2" \
  --data-urlencode "peid={PE_ID}" \
  --data-urlencode "dlttemplateid={DLT_TEMPLATE_ID}"

Delivery check

curl -sS -L -G "https://ultronsms.com/api/mt/GetDelivery" \
  --data-urlencode "user=${ULTRONSMS_USER}" \
  --data-urlencode "password=${ULTRONSMS_PASSWORD}" \
  --data-urlencode "jobid={JOB_ID}"

Response handling

Success criteria

API Success signal
SendSMS ErrorCode == "000", non-empty JobId
GetDelivery DeliveryReports[0].DeliveryStatus == "Delivered", ErrorCode == "000"

Common errors

ErrorCode Meaning Action
7 Invalid username/password Check env credentials
25 Server NullReferenceException Usually invalid route or malformed request
Non-000 on delivery Operator / DLT rejection Verify template text, PE ID, sender binding

Adding a new template

  1. Register header and content on the DLT portal; wait for Approved status.
  2. Note Sender ID, PE ID, and Template ID from the Ultronsms dashboard.
  3. Add a DltTemplate entry to your registry.
  4. Implement a message builder that produces text identical to the approved template (variables filled only).
  5. Send one test SMS to a real number; confirm delivery via GetDelivery.
  6. Only then enable in production.

Security notes

  • Load ULTRONSMS_PASSWORD from secrets manager or .env (gitignored).
  • Never expose gateway credentials to frontend or mobile apps.
  • Log JobId and MessageId for support; avoid logging full message content if it contains OTPs or passwords.
  • Use the demo account (GENZENITHDemo) for staging only.