Docs

NepalOTP Documentation

OTP-first verification infrastructure for Nepal.

What is NepalOTP?

NepalOTP is a purpose-built OTP verification platform designed specifically for the Nepali market. It handles the complete lifecycle of one-time passwords: generation, delivery via SMS, and verification. The platform treats OTP as critical infrastructure rather than a messaging feature.

Why OTP is treated as infrastructure

OTP verification is a security primitive. It requires predictable delivery, strict expiration enforcement, and abuse protection. NepalOTP separates OTP from general-purpose SMS gateways because verification demands different reliability guarantees than transactional messaging.

What NepalOTP provides

  • Server-side OTP generation with cryptographic randomness
  • OTP delivery via SMS to Nepali phone numbers
  • Server-side OTP verification with attempt limits
  • Sandbox mode for development and testing
  • Rate limiting and abuse protection
  • Approved transactional SMS templates for non-OTP use cases

What NepalOTP does not support

  • Bulk SMS campaigns or mass messaging
  • Marketing or promotional SMS
  • Free-text SMS composition
  • Email delivery
  • Unapproved message content

Getting Started

1. Create an account

Register at nepalotp.com/login, verify your email, and complete your profile. Enable two-factor authentication from Settings → Two-Factor for additional security.

2. Create your first app

Go to Dashboard → Apps & API Keys and create an application. Choose the environment (sandbox or production), configure rate limits, and optionally add a webhook URL. Each app has a public app_id used in logs and webhook payloads.

3. Generate an API key

Open your app and create an API key. Keys are environment-specific and must match the app environment.

API Key Format

npot_test_1a2b3c4d5e6f7g8h9i0j

4. Send your first sandbox OTP

Use the API key to send an OTP. Sandbox mode always returns OTP 123456 and does not deduct credits.

5. Go live

Switch your app environment to production, top up credits, and generate a live API key (prefixed with npot_live_). Use the live key in production traffic.

Authentication

All API requests require authentication via an API key passed in the Authorization header.

Request Header
Authorization: Bearer notp_live_sk_1a2b3c4d5e6f7g8h9i0j

Key rotation

You can have up to 3 active API keys at any time. When rotating keys:

  1. Generate a new key in the dashboard
  2. Deploy your application with the new key
  3. Revoke the old key once migration is complete

Security best practices

  • Never expose API keys in client-side code
  • Store keys in environment variables or secure vaults
  • Use different keys for staging and production
  • Rotate keys periodically and after team member departures

Core Concepts

OTP Lifecycle

1

Generate

Server creates OTP

2

Send

SMS delivered

3

Verify

User submits code

4

Expire

OTP invalidated

Server-side generation and verification

OTPs are generated and verified exclusively on NepalOTP servers. Your application never stores OTPs. When a user submits a code, your backend sends it to NepalOTP for verification.

Expiry and attempt limits

Parameter Default Description
expiry 5 minutes Time until OTP becomes invalid
max_attempts 3 Failed attempts before OTP is invalidated
length 6 digits OTP length (fixed)

Why clients should never store OTPs

Storing OTPs client-side creates security vulnerabilities. Client storage can be inspected, logged, or intercepted. Server-side verification ensures the OTP is validated against a secure source of truth.

Sending an OTP

POST /v1/otp/send

Generates a new OTP and sends it via SMS to the specified phone number. The OTP message uses a fixed template format.

Request parameters

Parameter Type Required Description
phone string Yes Nepali phone number (10 digits, starting with 97 or 98)
channel string No Delivery channel. Default: sms
reference string No Your internal reference ID for tracking

Fixed OTP template

Your verification code is: {OTP}. Valid for 5 minutes. Do not share this code.

Example request

cURL
curl -X POST https://api.nepalotp.com/v1/otp/send \
  -H "Authorization: Bearer notp_live_sk_1a2b3c4d5e6f7g8h9i0j" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "9841234567",
    "reference": "user_signup_abc123"
  }'

Success response

200 OK
{
  "success": true,
  "data": {
    "otp_id": "otp_8f9a7b6c5d4e3f2a",
    "phone": "9841234567",
    "expires_at": "2024-01-15T10:35:00Z",
    "attempts_remaining": 3
  }
}

Common errors

400 INVALID_PHONE_FORMAT

Phone number must be a 10-digit Nepali number starting with 97 or 98.

429 RATE_LIMIT_EXCEEDED

Too many OTP requests for this phone number. Wait before retrying.

Verifying an OTP

POST /v1/otp/verify

Verifies an OTP submitted by the user. The OTP is invalidated after successful verification or when the maximum attempt limit is reached.

Request parameters

Parameter Type Required Description
otp_id string Yes The OTP ID returned from the send endpoint
code string Yes The 6-digit code entered by the user

Example request

cURL
curl -X POST https://api.nepalotp.com/v1/otp/verify \
  -H "Authorization: Bearer notp_live_sk_1a2b3c4d5e6f7g8h9i0j" \
  -H "Content-Type: application/json" \
  -d '{
    "otp_id": "otp_8f9a7b6c5d4e3f2a",
    "code": "847293"
  }'

Success response

200 OK
{
  "success": true,
  "data": {
    "verified": true,
    "phone": "9841234567"
  }
}

Failure response

400 Bad Request
{
  "success": false,
  "error": {
    "code": "INVALID_OTP",
    "message": "The code is incorrect",
    "attempts_remaining": 2
  }
}

Sandbox Mode

Sandbox mode allows you to integrate and test the NepalOTP API without sending real SMS messages or incurring charges.

Sandbox behavior

No SMS delivery

Messages are logged but not sent to real phones.

Fixed OTP: 123456

All sandbox OTPs are 123456 for predictable testing.

No balance deduction

Your wallet balance is not affected by sandbox requests.

Identical API responses

Response structure matches production exactly.

Identifying sandbox mode

Sandbox API keys are prefixed with notp_sandbox_ while live keys use notp_live_.

Moving to live mode

  1. Complete your integration testing in sandbox mode
  2. Navigate to Dashboard → Settings → Live Mode
  3. Submit a request describing your use case and expected volume
  4. Add funds to your wallet once approved
  5. Generate a live API key and deploy

Transactional SMS Templates

Transactional SMS templates allow you to send non-OTP messages for specific operational use cases. All templates must be manually approved before use.

Transactional SMS is not OTP

OTP messages use a fixed system template. Transactional SMS is a separate feature for sending approved operational messages like order confirmations or appointment reminders.

Allowed use cases

Order confirmations

Order placed, shipped, delivered

Payment receipts

Payment received, refund processed

Appointment reminders

Upcoming appointments, schedule changes

Account alerts

Security alerts, password changes

Template restrictions

  • No links or URLs
  • No promotional content or offers
  • No free-text composition at runtime
  • No bulk campaigns

Template approval process

  1. Submit template via Dashboard → Templates → Create
  2. Include template name, content, and use case description
  3. Review takes 1-2 business days
  4. Approved templates receive a template_id

Example template submission

Template Content
Your order {{order_id}} has been shipped. 
Expected delivery: {{delivery_date}}. 
Thank you for shopping with {{brand_name}}.

Sending a transactional SMS

POST /v1/sms/send
cURL
curl -X POST https://api.nepalotp.com/v1/sms/send \
  -H "Authorization: Bearer notp_live_sk_1a2b3c4d5e6f7g8h9i0j" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "9841234567",
    "template_id": "tpl_order_shipped",
    "variables": {
      "order_id": "ORD-2024-0847",
      "delivery_date": "Jan 18, 2024",
      "brand_name": "ShopNP"
    }
  }'

Webhooks

Webhooks let you receive real-time delivery and verification updates for OTP and SMS without polling. Configure a webhook URL per app and we will POST signed events whenever activity occurs.

Enable webhooks

  1. Open Dashboard → Apps → Manage
  2. Enable Webhooks and provide a webhook_url
  3. Set a webhook_secret to sign requests (recommended)
  4. Return a 2xx response quickly to acknowledge delivery

Events

otp.sent

OTP delivered to provider successfully.

otp.failed

OTP delivery failed.

otp.verified

OTP verified by the end user.

sms.sent

Transactional SMS delivered to provider.

sms.failed

Transactional SMS delivery failed.

Example payload

JSON
{
  "id": "evt_9d8a7c6b5a4f3e2d",
  "event": "otp.sent",
  "created_at": "2026-01-04T16:42:00+05:45",
  "app": {
    "app_id": "app_k2Y9s0XrT3aB",
    "name": "Demo App",
    "environment": "sandbox"
  },
  "data": {
    "otp_id": "otp_8f9a7b6c5d4e3f2a",
    "phone": "+9779812345678",
    "reference": "login-123",
    "status": "sent",
    "is_sandbox": true,
    "cost": "0.0000",
    "expires_at": "2026-01-04T16:47:00+05:45",
    "sent_at": "2026-01-04T16:42:00+05:45"
  }
}

Signature verification

We sign webhook requests when a webhook_secret is set. Verify the signature using the raw request body and the X-Npot-Signature header.

PHP
$signature = $_SERVER['HTTP_X_NPOT_SIGNATURE'] ?? '';
$timestamp = $_SERVER['HTTP_X_NPOT_TIMESTAMP'] ?? '';
$payload = file_get_contents('php://input');

$expected = hash_hmac('sha256', $timestamp . '.' . $payload, $webhookSecret);
$header = "t={$timestamp},v1={$expected}";

if (! hash_equals($header, $signature)) {
    http_response_code(400);
    exit('Invalid signature');
}

Retries & timeouts

We retry failed webhooks up to 3 times with backoff. Your endpoint should respond within 10 seconds and return a 2xx status. On cPanel, you can keep delivery synchronous by using the sync queue driver or run a cron-based queue worker for higher throughput.

Template Variables & Validation

Template variables allow dynamic content insertion within approved templates. Variables are validated at runtime to ensure message integrity.

Variable syntax

Variables use double curly brace syntax: {{variable_name}}

Naming rules

  • Lowercase letters, numbers, and underscores only
  • Must start with a letter
  • Maximum 32 characters

Value rules

Constraint Limit
Maximum value length 128 characters
Allowed characters Alphanumeric, spaces, common punctuation
Disallowed URLs, special characters, control characters

Runtime enforcement

When sending a transactional SMS, the API validates that:

  • All required variables are provided
  • No extra variables are included
  • Values conform to length and character rules

Validation error examples

Missing required variable
{
  "success": false,
  "error": {
    "code": "MISSING_VARIABLE",
    "message": "Required variable 'order_id' is missing"
  }
}
Unknown variable
{
  "success": false,
  "error": {
    "code": "UNKNOWN_VARIABLE",
    "message": "Variable 'promo_code' is not defined in template"
  }
}

Rate Limits & Abuse Protection

Rate limits protect the platform and end users from abuse. Limits are enforced at multiple levels.

OTP rate limits

Scope Limit Window
Per phone number 5 OTPs 1 hour
Per phone number 10 OTPs 24 hours
Per API key 100 requests 1 minute
Per API key 10,000 requests 24 hours

Transactional SMS limits

Scope Limit Window
Per phone per template 3 messages 1 hour
Per API key 1,000 messages 24 hours

Why limits exist

Rate limits prevent SMS bombing attacks against individual phone numbers, protect carrier relationships, and ensure fair usage across all customers.

Rate limit response

429 Too Many Requests
{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many OTP requests for this phone number",
    "retry_after": 3600
  }
}

Pricing & Billing

NepalOTP uses a prepaid wallet model. You add funds to your account and usage is deducted in real time.

Prepaid wallet

Add funds via the dashboard using supported payment methods. Your wallet balance is displayed on the dashboard and available via API.

Billing behavior

Message type Sandbox Live
OTP SMS No charge Deducted from wallet
Transactional SMS No charge Deducted from wallet

No postpaid usage

API requests fail when wallet balance is insufficient. There is no credit or postpaid billing. Monitor your balance and set up low-balance alerts in the dashboard.

Errors & Status Codes

All API errors return a consistent structure with an error code and human-readable message.

Error response structure

{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human readable description"
  }
}

Error code reference

400 INVALID_PHONE_FORMAT

Phone number is not a valid 10-digit Nepali number.

400 INVALID_OTP

The submitted OTP code is incorrect.

400 OTP_EXPIRED

The OTP has expired. Request a new one.

400 MAX_ATTEMPTS_EXCEEDED

Too many failed verification attempts. OTP is invalidated.

401 INVALID_API_KEY

API key is missing, malformed, or revoked.

402 INSUFFICIENT_BALANCE

Wallet balance is too low to complete the request.

404 OTP_NOT_FOUND

The specified otp_id does not exist.

404 TEMPLATE_NOT_FOUND

The specified template_id does not exist or is not approved.

429 RATE_LIMIT_EXCEEDED

Too many requests. Check the retry_after field.

500 INTERNAL_ERROR

An unexpected error occurred. Contact support if persistent.

Best Practices

OTP UX recommendations

  • Show a countdown timer displaying time until expiry
  • Display remaining verification attempts
  • Provide a "Resend OTP" button with a cooldown (30-60 seconds)
  • Use auto-focus on OTP input fields

Retry and resend handling

  • Implement client-side rate limiting for resend buttons
  • Handle 429 errors gracefully with user-friendly messages
  • Use exponential backoff for automatic retries

When to use OTP vs transactional SMS

Use case Recommended
Phone verification at signup OTP
Two-factor authentication OTP
Password reset OTP
Order shipped notification Transactional SMS
Appointment reminder Transactional SMS

What not to do

  • Never log OTP codes in your application
  • Never store OTPs in client-side storage
  • Never expose API keys in frontend code
  • Never allow unlimited OTP resends without cooldown

FAQ

Why is NepalOTP OTP-first?

OTP verification has different requirements than general messaging: strict delivery timing, abuse protection, and security guarantees. Building OTP as the core product allows us to optimize for these requirements.

Can I send marketing SMS?

No. NepalOTP does not support marketing, promotional, or bulk SMS campaigns. All messaging must be transactional and use approved templates.

Can I edit templates after approval?

No. Approved templates cannot be modified. To change a template, submit a new template for approval and update your code to use the new template_id.

How many templates can I create?

Standard accounts can have up to 20 active templates. Contact support if you need more.

How long do OTPs last?

OTPs expire after 5 minutes. This is a fixed value and cannot be configured.

Will WhatsApp OTP be supported?

WhatsApp as a delivery channel is planned for a future release. Currently, only SMS delivery is available.

Support & Contact

Contact support

For technical issues, integration questions, or account inquiries:

support@nepalotp.com

When contacting support, include:

  • Your account email or organization name
  • Request ID from the API response (if applicable)
  • Timestamp of the issue
  • Error code and message received
  • Steps to reproduce the issue