urmi-ai/URMIOTP_API.md
2026-10-07 02:50:28 +05:30

11 KiB
Raw Permalink Blame History

UrmiOTP — developer API

Unified OTP API for Urmi developers. Use this document to integrate OTP delivery into your application.

Related docs

Document Purpose
OTP_SMS_IMPLEMENTATION.md Direct Ultronsms gateway integration (standalone, not used by UrmiOTP API today)
GENERIC_TEMPLATE_SMS.md Generic DLT template SMS via Ultronsms
/api/v1/messages/docs/send Per-account DLT SMS API (actually delivers SMS today)

How it is implemented

UrmiOTP is a project-gated, template-based OTP layer on top of the Urmi control-plane API. It is not a standalone SMS gateway integration.

Architecture

┌──────────────────┐   POST /api/v1/otp/generate   ┌─────────────────┐
│  Developer app   │ ────────────────────────────► │  routes_otp.py  │
│  (API key auth)  │                               └────────┬────────┘
└──────────────────┘                                        │
                                                              ▼
┌──────────────────┐   POST /api/v1/urmiotp/generate ┌─────────────────┐
│  Urmi console    │ ───────────────────────────────►│ routes_urmiotp  │
│  (session auth)  │                                 └────────┬────────┘
└──────────────────┘                                          │
                                                              ▼
                                                    ┌─────────────────┐
                                                    │  otp_service.py │
                                                    │ generate_otp_   │
                                                    │   for_user()    │
                                                    └────────┬────────┘
                                                             │
                    ┌────────────────────────────────────────┼────────────────────────┐
                    ▼                    ▼                   ▼                        ▼
           urmiotp_catalog.py    UrmiOtpProject      normalize_recipients()   record_otp_log()
           (7 hardcoded          (approved project     (phone formatting)       → otp_message_logs
            templates)            lookup)

Both entry points call the same core function: generate_otp_for_user() in backend/app/otp_service.py.

Onboarding flow

  1. Developer creates an UrmiOTP project in the console (POST /api/v1/urmiotp/projects).
    • Provides project_name, message_label (product name shown in SMS), app_type, app_url, and purpose.
    • Project starts with status: pending.
  2. Admin reviews and approves the project (PATCH /api/v1/admin/compliance/urmiotp-projects/{id}).
    • Only approved projects can call the generate API.
  3. Developer creates an API key (urmi_live_...) under Develop → API Keys.
  4. Developer calls POST /api/v1/otp/generate with the project name, template type, recipient, and OTP variables.

Per-project documentation (templates with your message_label filled in) is available at:

GET /api/v1/urmiotp/projects/{project_id}/documentation

(Requires console session auth; available only after approval.)

Message composition

Templates are defined in backend/app/urmiotp_catalog.py — seven fixed DLT-style message patterns. Each template has:

  • {message_label} — replaced with the project's registered label (e.g. Acme App)
  • {#var#} × 2 — replaced in order with variables.otp and variables.validity_minutes

Example for login_authentication with label Acme:

Your URMI login verification code for Acme is 583921. It is valid for 10 minutes. - URMI

Urmi does not generate the OTP code. Your backend must create the numeric code, pass it in variables.otp, store it server-side with a TTL, and verify it when the user submits it.

What happens on generate

When POST /api/v1/otp/generate is called, otp_service.py:

  1. Validates template_type against the catalog.
  2. Loads the caller's approved UrmiOtpProject by project_name.
  3. Normalizes the recipient number (10-digit Indian numbers get 91 prefix).
  4. Builds the final SMS text via _build_otp_message().
  5. Writes a row to otp_message_logs with status: accepted.
  6. Returns message_preview and log_id.

SMS delivery

generate_otp_for_user() sends SMS via UltronsmsClient.send_template_sms() using the DLT-approved template from OTP_SMS_IMPLEMENTATION.md:

Your URMI app verification code is {otp} for {message_label}. It is valid for {validity_minutes} minutes. Do not share it to anyone - URMI AI
Setting Value
Sender ID URMIAI
PE ID 1701178780498045011
DLT template ID 1777178797989033323

template_type (e.g. login_authentication) is used for logging and analytics only. All types deliver the same DLT-registered SMS body.

Credentials come from ULTRONSMS_USER and ULTRONSMS_PASSWORD in the server environment.

Observability

OTP requests are stored in otp_message_logs and surfaced in:

  • Develop → Logs (GET /api/v1/logs/otp)
  • Develop → Analytics (grouped by project and template type)

API reference

Generate OTP

Method POST
Path /api/v1/otp/generate
Auth API key (Authorization: Bearer urmi_live_... or X-Api-Key: urmi_live_...)
Content-Type application/json

Request body

{
  "project_name": "acme-login",
  "template_type": "login_authentication",
  "to": "919999999999",
  "variables": {
    "otp": "583921",
    "validity_minutes": "10"
  }
}
Field Type Required Description
project_name string yes Your approved UrmiOTP project name (3–30 chars)
template_type string yes One of the template types below
to string yes Recipient mobile with country code (10–15 digits)
variables.otp string yes OTP value to embed in the message
variables.validity_minutes string yes Validity duration in minutes

Template types

template_type Use case
general_verification Signup, KYC, general verification
login_authentication Login, MFA, 2FA
account_recovery Forgot password, account recovery
account_change Change phone, email, or password
action_authorization Sensitive actions, API key changes
transaction_verification Payments, transfers
device_verification Device pairing, IoT activation

Success response (200)

{
  "success": true,
  "log_id": "550e8400-e29b-41d4-a716-446655440000",
  "project_name": "acme-login",
  "template_type": "login_authentication",
  "to": "919999999999",
  "message_preview": "Your URMI app verification code is 583921. It is valid for 10 minutes. Do not share it to anyone - URMI AI",
  "status": "sent",
  "job_id": "215488915",
  "message_id": "5h6krUyQsU64iM0otKX7kA"
}

Error responses

HTTP Cause
401 Missing or invalid API key
403 Account inactive
404 UrmiOTP project not found for this user
422 Project not approved, unknown template_type, missing variables, or invalid phone number

Authentication

Create an API key in the Urmi console under Develop → API Keys.

Authorization: Bearer urmi_live_<your_key>

Alternate header:

X-Api-Key: urmi_live_<your_key>

API keys use the urmi_live_ prefix. Implementation: backend/app/api_key_auth.py.


Examples

cURL

curl -X POST "https://urmiai.com/api/v1/otp/generate" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer urmi_live_YOUR_KEY_HERE" \
  -d '{
    "project_name": "acme-login",
    "template_type": "login_authentication",
    "to": "919999999999",
    "variables": {
      "otp": "583921",
      "validity_minutes": "10"
    }
  }'

Local development base URL: http://localhost:8000

Python

import httpx

API_BASE = "https://urmiai.com"
API_KEY = "urmi_live_YOUR_KEY_HERE"

response = httpx.post(
    f"{API_BASE}/api/v1/otp/generate",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "project_name": "acme-login",
        "template_type": "login_authentication",
        "to": "919999999999",
        "variables": {"otp": "583921", "validity_minutes": "10"},
    },
    timeout=30,
)
response.raise_for_status()
print(response.json())

Node.js

const response = await fetch("https://urmiai.com/api/v1/otp/generate", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer urmi_live_YOUR_KEY_HERE",
  },
  body: JSON.stringify({
    project_name: "acme-login",
    template_type: "login_authentication",
    to: "919999999999",
    variables: { otp: "583921", validity_minutes: "10" },
  }),
});
const data = await response.json();

Console testing (no API key)

For testing inside the Urmi console or Develop → Testing Center, use the session-authenticated variant:

Method POST
Path /api/v1/urmiotp/generate
Auth Console session cookie

Same request body as /api/v1/otp/generate.


Integration checklist

  • Create and get an UrmiOTP project approved
  • Create an API key (urmi_live_...)
  • Generate OTP codes on your backend (Urmi does not generate them)
  • Store OTP server-side with TTL matching validity_minutes
  • Verify user-submitted OTP on your backend before marking session verified
  • Rate-limit OTP requests per phone number and IP
  • Do not return the OTP in your own API responses to the client
  • Do not log full OTP values in application logs
  • Handle non-200 responses with a generic user message ("Unable to send OTP, try again")

Source files

File Role
backend/app/routes_otp.py External API route (/api/v1/otp/generate)
backend/app/routes_urmiotp.py Console routes (projects, docs, session generate)
backend/app/otp_service.py Core generate logic
backend/app/urmiotp_catalog.py Template catalog and API metadata
backend/app/log_service.py record_otp_log() → otp_message_logs
backend/app/models.py UrmiOtpProject, OtpMessageLog models
backend/app/api_key_auth.py API key authentication
frontend/src/features/agent/UrmiOtpDocsPage.tsx In-console API documentation UI